Eevis BlogPosts about accessibility, being a (woman) developer and life in general.2024-03-16T00:00:00.000Zhttps://eevis.codes/blogEevis Panulahello@eevis.codesWhy Playing Team Sports Makes Me a Better Developer2021-05-12T00:00:00.000Zhttps://eevis.codes/blog/2021-05-12/why-playing-team-sports-makes-me-a-better-developer/
<nav id="breadcrumbs" aria-label="breadcrumbs">
<ol>
<li>
<a href="https://eevis.codes/blog/">Blog</a>
<span aria-hidden="true" class="breadcrumb-separator">/<span>
</span></span></li>
<li>Why Playing Team Sports Makes Me a Better Developer</li><li>
</li></ol>
</nav>
<article class="blogPostLong">
<h1 id="/2021-05-12/why-playing-team-sports-makes-me-a-better-developer">Why Playing Team Sports Makes Me a Better Developer</h1>
<div class="reading-time-and-date">
<p>
Reading time: about 6 minutes
</p>
<span class="separator"></span>
<p>Published <time datetime="12th May, 2021">12th May, 2021</time></p>
</div>
<section class="likes-and-boosts">
<div>
<svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="boosts-title">
<title id="boosts-title">Boosts</title>
<path d="M7.19 4.35047C5.15 6.64047 3.75 9.93047 3.62 10.2405L0 8.69047L4.05 4.64047C4.52 4.17047 5.2 3.96047 5.86 4.09047L7.19 4.35047ZM9.17 15.0005C9.17 15.0005 12.91 13.4505 15.06 11.3005C20.46 5.90047 19.56 1.68047 19.27 0.730473C18.32 0.430473 14.1 -0.459527 8.7 4.94047C6.55 7.09047 5 10.8305 5 10.8305L9.17 15.0005ZM15.65 12.8105C13.36 14.8505 10.07 16.2505 9.76 16.3805L11.31 20.0005L15.36 15.9505C15.83 15.4805 16.04 14.8005 15.91 14.1405L15.65 12.8105ZM7 16.0005C7 16.8305 6.66 17.5805 6.12 18.1205C4.94 19.3005 0 20.0005 0 20.0005C0 20.0005 0.7 15.0605 1.88 13.8805C2.2988 13.4595 2.83325 13.1724 3.4155 13.0558C3.99775 12.9392 4.60153 12.9982 5.15017 13.2253C5.69882 13.4525 6.16758 13.8376 6.49694 14.3317C6.8263 14.8258 7.0014 15.4067 7 16.0005ZM11 7.00047C11 5.90047 11.9 5.00047 13 5.00047C14.1 5.00047 15 5.90047 15 7.00047C15 8.10047 14.1 9.00047 13 9.00047C11.9 9.00047 11 8.10047 11 7.00047Z" fill="#00696D"></path>
</svg>
0
</div>
<div>
<svg role="img" width="20" height="19" viewBox="0 0 20 19" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="likes-title">
<title id="likes-title">Likes</title>
<path d="M10 18.35L8.55 17.03C3.4 12.36 0 9.27 0 5.5C0 2.41 2.42 0 5.5 0C7.24 0 8.91 0.81 10 2.08C11.09 0.81 12.76 0 14.5 0C17.58 0 20 2.41 20 5.5C20 9.27 16.6 12.36 11.45 17.03L10 18.35Z"></path>
</svg>
0
</div>
</section>
<img class="cover-photo" src="https://images.ctfassets.net/mpqufjsy02zr/1GPr43cyVEcjlxXIN71vWb/8ae5869bb06b28bc4ec1271420c36811/isaiah-rustad-8q5rl4rdK_0-unsplash.jpg" alt="Five people, assumingly women, standing in line, holding hands. They are wearing sports clothes: Long-sleeved white shirts with numbers on the front of the shirts, and short black shorts with a Nike logo on them." />
<div class="blog-tags">
<a href="https://eevis.codes/tags/career"><span class="blog-tag purple">career</span></a> <a href="https://eevis.codes/tags/general"><span class="blog-tag turquoise">general</span></a> <a href="https://eevis.codes/tags/webdev"><span class="blog-tag blue">webdev</span></a>
</div>
<nav aria-label="Why Playing Team Sports Makes Me a Better Developer Table of Contents" class="table-of-contents">
<h2>Table of Contents</h2>
<ol>
<li><a href="https://eevis.codes/blog/2021-05-12/why-playing-team-sports-makes-me-a-better-developer/#team-spirit-of-the-game"> Team Spirit (of the Game)</a></li><li><a href="https://eevis.codes/blog/2021-05-12/why-playing-team-sports-makes-me-a-better-developer/#perseverance"> Perseverance</a></li><li><a href="https://eevis.codes/blog/2021-05-12/why-playing-team-sports-makes-me-a-better-developer/#willingness-to-do-voluntary-work"> Willingness to Do Voluntary Work</a></li><li><a href="https://eevis.codes/blog/2021-05-12/why-playing-team-sports-makes-me-a-better-developer/#getting-away-from-work-thoughts"> Getting Away from Work Thoughts</a></li><li><a href="https://eevis.codes/blog/2021-05-12/why-playing-team-sports-makes-me-a-better-developer/#keeping-my-brain-functional"> Keeping My Brain Functional</a></li><li><a href="https://eevis.codes/blog/2021-05-12/why-playing-team-sports-makes-me-a-better-developer/#wrapping-up"> Wrapping Up</a></li>
</ol>
</nav>
<p>I've played team sports since I was something like ten. First, it was <a href="https://en.wikipedia.org/wiki/Floorball">floorball</a> - this fantastic indoor sport, where you have a stick, a ball, and goals. Something similar to hockey, but less violent. And after all, completely different. </p>
<p>Then I moved to another city and soon realized that it's not as fun as it used to be with the team I had played with for 10 years. I switched to <a href="https://en.wikipedia.org/wiki/Ultimate_(sport)">ultimate frisbee</a>, which is something I still play. I also did a tour in the roller derby world, loved the sport but got a brain injury. I realized that if I want to be able to do my job, I need to let this awesome thing go. I'm sometimes still sad about it.</p>
<p>So, I have a history (and hopefully future) with team sports. I've done some coaching during the years and participated in different boards - be it a club or national level organization. So I could say I've gotten a lot from this and also learned a ton. </p>
<p>One day, while I was doing some ladder drills (they're for agility on the field), I started thinking if there has been anything I've used from my sports background in my day-to-day job. After pondering for a while, I realized that yes, there is. In this blog post, I will share some of those thoughts.</p>
<p>Many other activities may provide just the same benefits. However, my background is in team sports, so that's why my angle is from the perspective of team sports.</p>
<h2 id="team-spirit-of-the-game">Team Spirit (of the Game)</h2>
<p>If you're not familiar with ultimate frisbee, you might wonder what the heck the title of this section means. Let me explain. In ultimate frisbee, there is this thing called <a href="https://en.wikipedia.org/wiki/Ultimate_(sport)#Spirit_of_the_game">"Spirit of the Game,"</a> which has to do with, for example, fairness of the game and sportsmanship. It's something that makes the sport possible to play without any referees (yup, you read it right!). </p>
<p>During the years, being part of a team has indeed taught me to be a better team member. Especially being part of teams that comply with such philosophy as the Spirit of the Game. I know how to work in a group, and also that different people have distinct strengths. Also, I've learned to get along with a very diverse set of characters - not all of them "click" with me, but still, we are a team, and we need to work together. </p>
<p>Team sports are never about one individual. There are 5-7-ish people on the field (depending on the sport), and everyone is needed to score. This makes me try to do my best and never let the team down. </p>
<h2 id="perseverance">Perseverance</h2>
<p>As the season approaches, there is usually some kind of goal. Some years, for my team, it has been the national championships. That goal has been the guide for every practice and game. It's a long time from the pre-season to the final match, and perseverance is needed. </p>
<p>The same has been true with every development project I've been in. Development projects tend to be long, and there are always moments when I want to give up. Learning perseverance from sports helps me to put those moments in context. It also gets me through that valley of whatever it is that makes me feel like giving up. </p>
<h2 id="willingness-to-do-voluntary-work">Willingness to Do Voluntary Work</h2>
<p>I grew up in an environment where doing voluntary work for the club was expected. So I've spent countless days raising money, officiating games, cleaning up places, and so forth. Whenever there is a new "working together day" with my club, my first thoughts are, "ok, this is mandatory." (I'm not sure of the correct translation of the word; for those who understand Finnish, I mean "talkoot.")</p>
<p>I've come to realize that this is not the case for everyone. Of course, not everyone can give their time, because sometimes life gets in the way, but I'm talking more about attitude here.</p>
<p>I've been reflecting on the topic a lot during the past couple of weeks. On Friday 23rd of April, I received the first-ever Mimmit Koodaa-award for my work for the <a href="https://mimmitkoodaa.ohjelmistoebusiness.fi/in-english/">Mimmit Koodaa-community</a>. This community (translates roughly "Women code") is helping to bring more women into tech.</p>
<p>The justification for me getting the award was all the hard work I've done for the community. I've gotten so many praises, and "yeah, it was definitely the right person who got the award"s. However, every time I hear, "you've worked so hard!" I instantly think, "Umm... but isn't that something that every single one would do?". Apparently not. </p>
<p>So that's the attitude and position where I am because of my background. This is not to brag or anything. I'm just trying to reflect on some thoughts and how my experience affects the work I do - at my day job and outside of it. </p>
<h2 id="getting-away-from-work-thoughts">Getting Away from Work Thoughts</h2>
<p>While the previous ones are something that I'm going to carry with me for the rest of my life, the next ones are about the direct consequences of doing sports.</p>
<p>After a long, intensive day at work, it is sometimes hard to let go of those work-related thoughts. They just keep going around and around and make me anxious because I know I shouldn't be thinking about them and because I can't do anything to them at that point. Going into practice, where I need to give 100% of my concentration to the drills and game, definitely helps me let go of those thoughts.</p>
<p>You could argue that any sporting activity would do. Well, for me, going for a run alone would mean that the thoughts are still there, and I would go even deeper into them because I'd be alone with those thoughts. However, being around people who have nothing to do with my work and doing something completely different helps me forget. </p>
<h2 id="keeping-my-brain-functional">Keeping My Brain Functional</h2>
<p>For developers, our brain is the most crucial tool for doing our jobs. I've faced the situation where I needed to give something up to keep the risk of losing anything more from the brain capacity I have. So I could say I've given some thought to this.</p>
<p>This point, however, is not just about protecting our heads from injuries. I'm talking about the effects actively moving has on our brain. As <a href="https://www.health.harvard.edu/blog/regular-exercise-changes-brain-improve-memory-thinking-skills-201404097110">Heidi Godman writes</a>:</p>
<blockquote>
<p>Exercise changes the brain in ways that protect memory and thinking skills.</p>
</blockquote>
<p>To be precise, the effects were visible from regular aerobic exercises. For example, muscle training didn't have the same results in the study. If you want to learn more, I highly recommend checking the article linked. </p>
<p>So, being part of a sports team also requires me to continuously be active, whether in-season or off-season. Luckily this is good for my brain health too. And, as said in the previous point, another part is that I have some mechanisms to let go of those work-related thoughts, so that's really good for my brain too.</p>
<h2 id="wrapping-up">Wrapping Up</h2>
<p>So, I get numerous benefits from team sports - both from my long history with them and from actively being part of a team. Some of them are skills that can be directly used in the teams and projects at work, and some of them help me to be healthy enough to do my job. </p>
<p>Coming back to the title of this blog post, you might wonder how this makes me a better developer while I haven't really touched coding at all. Well, the most important aspect of the development is not coding. I think being a good developer is more about communication and team skills. </p>
<p>Recovering after the day at work is essential. I've worked with many people on the edge of burnout. They're usually not the nicest members of the team to work with because of the stress and other symptoms they have. Heck, I've been one too, and I wouldn't want to work with me from those times. So taking care of yourself is not only for you, it is for your team too. And I believe that makes a better developer.</p>
<p>Do you play any sports? Or have you noticed benefits or learnings you get from leisure activities that are usable in your day-to-day job?</p>
<p><em>Photo by <a href="https://unsplash.com/@isaiahrustad?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Isaiah Rustad</a> on <a href="https://unsplash.com/?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a></em></p>
</article>
Let's Talk About Accessibility2021-05-21T00:00:00.000Zhttps://eevis.codes/blog/2021-05-21/lets-talk-about-accessibility/
<nav id="breadcrumbs" aria-label="breadcrumbs">
<ol>
<li>
<a href="https://eevis.codes/blog/">Blog</a>
<span aria-hidden="true" class="breadcrumb-separator">/<span>
</span></span></li>
<li>Let's Talk About Accessibility</li><li>
</li></ol>
</nav>
<article class="blogPostLong">
<h1 id="/2021-05-21/lets-talk-about-accessibility">Let's Talk About Accessibility</h1>
<div class="reading-time-and-date">
<p>
Reading time: about 6 minutes
</p>
<span class="separator"></span>
<p>Published <time datetime="21st May, 2021">21st May, 2021</time></p>
</div>
<section class="likes-and-boosts">
<div>
<svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="boosts-title">
<title id="boosts-title">Boosts</title>
<path d="M7.19 4.35047C5.15 6.64047 3.75 9.93047 3.62 10.2405L0 8.69047L4.05 4.64047C4.52 4.17047 5.2 3.96047 5.86 4.09047L7.19 4.35047ZM9.17 15.0005C9.17 15.0005 12.91 13.4505 15.06 11.3005C20.46 5.90047 19.56 1.68047 19.27 0.730473C18.32 0.430473 14.1 -0.459527 8.7 4.94047C6.55 7.09047 5 10.8305 5 10.8305L9.17 15.0005ZM15.65 12.8105C13.36 14.8505 10.07 16.2505 9.76 16.3805L11.31 20.0005L15.36 15.9505C15.83 15.4805 16.04 14.8005 15.91 14.1405L15.65 12.8105ZM7 16.0005C7 16.8305 6.66 17.5805 6.12 18.1205C4.94 19.3005 0 20.0005 0 20.0005C0 20.0005 0.7 15.0605 1.88 13.8805C2.2988 13.4595 2.83325 13.1724 3.4155 13.0558C3.99775 12.9392 4.60153 12.9982 5.15017 13.2253C5.69882 13.4525 6.16758 13.8376 6.49694 14.3317C6.8263 14.8258 7.0014 15.4067 7 16.0005ZM11 7.00047C11 5.90047 11.9 5.00047 13 5.00047C14.1 5.00047 15 5.90047 15 7.00047C15 8.10047 14.1 9.00047 13 9.00047C11.9 9.00047 11 8.10047 11 7.00047Z" fill="#00696D"></path>
</svg>
0
</div>
<div>
<svg role="img" width="20" height="19" viewBox="0 0 20 19" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="likes-title">
<title id="likes-title">Likes</title>
<path d="M10 18.35L8.55 17.03C3.4 12.36 0 9.27 0 5.5C0 2.41 2.42 0 5.5 0C7.24 0 8.91 0.81 10 2.08C11.09 0.81 12.76 0 14.5 0C17.58 0 20 2.41 20 5.5C20 9.27 16.6 12.36 11.45 17.03L10 18.35Z"></path>
</svg>
0
</div>
</section>
<img class="cover-photo" src="https://images.ctfassets.net/mpqufjsy02zr/2PYSzzdBokMHTf4ywmO3D0/d2a4ac280c57ac252058b8c48d15a457/james-sutton-AcL5SitD8Wg-unsplash.jpg" alt="A blurred view with eyeglasses on a book. Text is unreadable, and the picture is grey-scaled." />
<div class="blog-tags">
<a href="https://eevis.codes/tags/a11y"><span class="blog-tag turquoise">a11y</span></a> <a href="https://eevis.codes/tags/html"><span class="blog-tag purple">html</span></a> <a href="https://eevis.codes/tags/webdev"><span class="blog-tag blue">webdev</span></a>
</div>
<nav aria-label="Let's Talk About Accessibility Table of Contents" class="table-of-contents">
<h2>Table of Contents</h2>
<ol>
<li><a href="https://eevis.codes/blog/2021-05-21/lets-talk-about-accessibility/#why-is-gaad-important"> Why is GAAD Important?</a></li><li><a href="https://eevis.codes/blog/2021-05-21/lets-talk-about-accessibility/#but-its-not-only-about-the-websites"> But it's Not Only About the Websites</a></li><li><a href="https://eevis.codes/blog/2021-05-21/lets-talk-about-accessibility/#so-what-can-i-do"> So What Can I Do?</a></li><li><a href="https://eevis.codes/blog/2021-05-21/lets-talk-about-accessibility/#wrapping-up"> Wrapping Up</a></li>
</ol>
</nav>
<p>Yesterday (20th of May 2021) marked the 10th <a href="https://globalaccessibilityawarenessday.org/about/">Global Accessibility Awareness Day</a>, celebrated on the third Thursday of each May. It started from <a href="https://mysqltalk.wordpress.com/2011/11/27/challenge-accessibility-know-how-needs-to-go-mainstream-with-developers-now/">a single blog post from Joe Devon</a> and has grown into a global event since then. </p>
<p>One example of what Global Accessibility Awareness Day, or GAAD, has achieved is <a href="https://diamond.la/gaadpledge/">GAAD Pledge</a>. The first company to take the pledge was Facebook, committing to make the React Native framework fully accessible.</p>
<h2 id="why-is-gaad-important">Why is GAAD Important?</h2>
<p>I believe raising awareness about accessibility is essential. I mean, if we, who are creating the web, aren't aware of these things, how in the heck can we assume the sites and apps will be built in a way that everyone, whether they have a disability or not, can use them?</p>
<p>But it is not just that. The importance of raising awareness is not that only developers would know; it's about the other roles too. If designers ignore accessibility, some things (say, color choices) are hard to fix on the developers' end. If the product owner cares more about IE6 users than accessibility, well, those bugs get prioritized. If the people who decide on the budget see other things as more important, developers can't fix that. </p>
<p>WebAIM has conducted its <a href="https://webaim.org/projects/million/">yearly analysis</a> on the top million sites on the internet. In February 2021, 97.4% of these sites had detectable accessibility errors. And as they've used an automated tool for the analysis, it doesn't even catch all the failures. (Automated testing tools catch only about <a href="https://accessibility.blog.gov.uk/2017/02/24/what-we-found-when-we-tested-tools-on-the-worlds-least-accessible-webpage/">15-40% of the accessibility failures.</a>) So this means that sites like Google, Twitter, Youtube, Facebook, and others on the top million sites list have many accessibility barriers. </p>
<p>About 1 billion people worldwide need websites to be designed and developed with accessibility in mind in order to be able to use those sites. If we don't do that, we are excluding a hell of a number of people. You know, actual people, and not just numbers. </p>
<h2 id="but-its-not-only-about-the-websites">But it's Not Only About the Websites</h2>
<p>Okay, I've been writing from a perspective of a (web) developer and touched on a couple of other roles that are part of creating sites and apps. How about the others? Don't they need to know about accessibility? Well, yes, they do. </p>
<h3 id="its-about-social-media">It's About Social Media</h3>
<p> Let's think about, for example, social media for a while. Even if the app or site is developed to be accessible, the content creators may make it inaccessible. Adding alternative texts or image captions is the content creators' responsibility, as are adding captions, transcripts, and audio descriptions for videos and audios. </p>
<p>If you are now wondering what the heck am I writing here, let me explain— a person who can't see the picture benefits from a descriptive image caption. You know, all the information they would get from that image if they saw it. </p>
<p>In the same way, if a person can't hear, due to being deaf or, as in my case, just browsing the Instagram stories without sound, having captions for those videos is crucial. Audio descriptions of a video are essential for those who can't see. And for those, who are, for example, deafblind, transcripts are a must. </p>
<p>And oh, why I'm using phrases like "those who can't see/hear" instead of, for example, a person who is blind or deaf, is because this goes further than that. As with all the accessibility improvements, we all could benefit. As mentioned, I rarely watch Instagram stories with a volume on, so when people add captions, that means I get the gist of the story. And then I might watch it, not just skip it.</p>
<h3 id="its-about-the-language">It's About the Language</h3>
<p>Another thing is the language we use. This topic is two-sided: First, I want to talk about plain language, and then a bit about ableism. </p>
<h4 id="plain-language">Plain Language</h4>
<p>International Plain Language Federation <a href="https://www.iplfederation.org/plain-language/">defines the plain language</a> with the following words:</p>
<blockquote>
<p>A communication is in plain language if its wording, structure, and design are so clear that the intended readers can easily find what they need, understand what they find, and use that information.</p>
</blockquote>
<p>So, this basically means avoiding jargon and getting to the point as fast as possible. If you're interested in learning more about writing clearly and simply, <a href="https://webaim.org/techniques/writing/">WebAIM has got you covered.</a>. </p>
<h4 id="ableism">Ableism</h4>
<p>Another point about the language I wanted to raise is ableism, especially in the language we use. There are certain phrases that are part of the language that we use without thinking. Like, have you ever said that "This week has been crazy!" or "I'm feeling autistic right now" or similar? Well, you know, that's not okay. </p>
<p>You might feel offended right now because you feel like I'm blaming you for something, but that's not the case. We all use language unconsciously, and if we've learned the phrases, they come naturally. So I'm not assuming that you're doing this on purpose, just out of not knowing. And that is fine because now you know and can do something about it. Changing the language we use is a long process (heck, I still make mistakes almost every day), but it is worth it.</p>
<p>Here is an excellent article from <a href="https://hbr.org/2020/12/why-you-need-to-stop-using-these-words-and-phrases">Rakshitha Arni Ravishankar</a> explaining more on the reasons why such language is harmful and what we can do about that. </p>
<h2 id="so-what-can-i-do">So What Can I Do?</h2>
<p>Okay, I got you there, right? And now you know that something needs to be done. It might feel like a lot and overwhelming, but let's take one step at a time. I could give a long list of things to do, but let's start with two.</p>
<h3 id="educate-yourself">Educate Yourself</h3>
<p>First, educate yourself. If you're someone in charge of creating sites or apps, learn how to make them accessible. Learn about people with disabilities and their needs. Listen to their stories. </p>
<p>I'm sharing some of my favorite resources here, both from the technical and more general perspective: </p>
<ul>
<li><a href="https://www.bemyeyes.com/podcasts-show/13-letters">13 Letters Podcast</a> - A podcast about accessibility with super interesting topics and guests</li>
<li><a href="https://www.a11yproject.com/resources/">The A11y Project Resources</a> - A huge collection of resources about accessibility. </li>
<li><a href="https://a11ytalks.com/">A11y Talks</a> - Virtual meetup about accessibility. Topics covered have been broad, from marketing to development.</li>
<li><a href="https://webaim.org/articles/">WebAIM Articles</a> - A collection of resources about disabilities, digital accessibility, laws, and other relevant things</li>
</ul>
<h3 id="check-your-content-and-language">Check Your Content and Language</h3>
<p>So, another thing that you could do is to check the content you create. It's possible to add alternative text to images. Here are links to the instructions in some significant social media sites: <a href="https://www.facebook.com/help/214124458607871">Facebook</a>, <a href="https://www.facebook.com/help/instagram/503708446705527">Instagram</a>, <a href="https://www.linkedin.com/help/linkedin/answer/109799/adding-alternative-text-to-images-for-accessibility?lang=en">LinkedIn</a>, <a href="https://help.twitter.com/en/using-twitter/picture-descriptions">Twitter</a>. </p>
<p>If you have videos or audio-only content (podcasts are a good example), make sure that everyone can use that content. Add captions, transcripts, and audio descriptions, whichever are needed for that particular content. </p>
<p>Also, be conscious of your language. If you find yourself using ableist phrases, start making an effort to change those phrases. There are always alternatives.</p>
<h2 id="wrapping-up">Wrapping Up</h2>
<p>I hope that this blog post has raised awareness about accessibility for you and that you've learned something new. It would be awesome to hear if that has happened, so please share your learnings! </p>
<p><em>Cover photo by <a href="https://unsplash.com/@jamessutton_photography?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">James Sutton</a> on <a href="https://unsplash.com/?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a></em></p>
</article>
Don't Develop Just for Yourself - A Developer's Checklist to Accessibility2021-05-28T00:00:00.000Zhttps://eevis.codes/blog/2021-05-28/dont-develop-just-for-yourself-a-developers-checklist-to-accessibility/
<nav id="breadcrumbs" aria-label="breadcrumbs">
<ol>
<li>
<a href="https://eevis.codes/blog/">Blog</a>
<span aria-hidden="true" class="breadcrumb-separator">/<span>
</span></span></li>
<li>Don't Develop Just for Yourself - A Developer's Checklist to Accessibility</li><li>
</li></ol>
</nav>
<article class="blogPostLong">
<h1 id="/2021-05-28/dont-develop-just-for-yourself-a-developers-checklist-to-accessibility">Don't Develop Just for Yourself - A Developer's Checklist to Accessibility</h1>
<div class="reading-time-and-date">
<p>
Reading time: about 8 minutes
</p>
<span class="separator"></span>
<p>Published <time datetime="28th May, 2021">28th May, 2021</time></p>
</div>
<section class="likes-and-boosts">
<div>
<svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="boosts-title">
<title id="boosts-title">Boosts</title>
<path d="M7.19 4.35047C5.15 6.64047 3.75 9.93047 3.62 10.2405L0 8.69047L4.05 4.64047C4.52 4.17047 5.2 3.96047 5.86 4.09047L7.19 4.35047ZM9.17 15.0005C9.17 15.0005 12.91 13.4505 15.06 11.3005C20.46 5.90047 19.56 1.68047 19.27 0.730473C18.32 0.430473 14.1 -0.459527 8.7 4.94047C6.55 7.09047 5 10.8305 5 10.8305L9.17 15.0005ZM15.65 12.8105C13.36 14.8505 10.07 16.2505 9.76 16.3805L11.31 20.0005L15.36 15.9505C15.83 15.4805 16.04 14.8005 15.91 14.1405L15.65 12.8105ZM7 16.0005C7 16.8305 6.66 17.5805 6.12 18.1205C4.94 19.3005 0 20.0005 0 20.0005C0 20.0005 0.7 15.0605 1.88 13.8805C2.2988 13.4595 2.83325 13.1724 3.4155 13.0558C3.99775 12.9392 4.60153 12.9982 5.15017 13.2253C5.69882 13.4525 6.16758 13.8376 6.49694 14.3317C6.8263 14.8258 7.0014 15.4067 7 16.0005ZM11 7.00047C11 5.90047 11.9 5.00047 13 5.00047C14.1 5.00047 15 5.90047 15 7.00047C15 8.10047 14.1 9.00047 13 9.00047C11.9 9.00047 11 8.10047 11 7.00047Z" fill="#00696D"></path>
</svg>
0
</div>
<div>
<svg role="img" width="20" height="19" viewBox="0 0 20 19" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="likes-title">
<title id="likes-title">Likes</title>
<path d="M10 18.35L8.55 17.03C3.4 12.36 0 9.27 0 5.5C0 2.41 2.42 0 5.5 0C7.24 0 8.91 0.81 10 2.08C11.09 0.81 12.76 0 14.5 0C17.58 0 20 2.41 20 5.5C20 9.27 16.6 12.36 11.45 17.03L10 18.35Z"></path>
</svg>
0
</div>
</section>
<img class="cover-photo" src="https://images.ctfassets.net/mpqufjsy02zr/66N9L5tLTVFLyCrBwPiWeo/68b88e9825a83d1653b9789d5a1ab343/glenn-carstens-peters-RLw-UC03Gwc-unsplash.jpg" alt="A notebook with a checklist. Hand holding a pen, writing a new item on the list. On the background, there's a blurred blue and white cloth." />
<div class="blog-tags">
<a href="https://eevis.codes/tags/a11y"><span class="blog-tag turquoise">a11y</span></a> <a href="https://eevis.codes/tags/html"><span class="blog-tag purple">html</span></a> <a href="https://eevis.codes/tags/webdev"><span class="blog-tag blue">webdev</span></a>
</div>
<nav aria-label="Don't Develop Just for Yourself - A Developer's Checklist to Accessibility Table of Contents" class="table-of-contents">
<h2>Table of Contents</h2>
<ol>
<li><a href="https://eevis.codes/blog/2021-05-28/dont-develop-just-for-yourself-a-developers-checklist-to-accessibility/#keyboard-navigation"> Keyboard Navigation</a></li><li><a href="https://eevis.codes/blog/2021-05-28/dont-develop-just-for-yourself-a-developers-checklist-to-accessibility/#alt-texts"> Alt-Texts</a></li><li><a href="https://eevis.codes/blog/2021-05-28/dont-develop-just-for-yourself-a-developers-checklist-to-accessibility/#page-language"> Page Language</a></li><li><a href="https://eevis.codes/blog/2021-05-28/dont-develop-just-for-yourself-a-developers-checklist-to-accessibility/#text-resizing"> Text Resizing</a></li><li><a href="https://eevis.codes/blog/2021-05-28/dont-develop-just-for-yourself-a-developers-checklist-to-accessibility/#color-alone"> Color Alone</a></li><li><a href="https://eevis.codes/blog/2021-05-28/dont-develop-just-for-yourself-a-developers-checklist-to-accessibility/#mobile-zooming"> Mobile Zooming</a></li><li><a href="https://eevis.codes/blog/2021-05-28/dont-develop-just-for-yourself-a-developers-checklist-to-accessibility/#captions-and-transcripts"> Captions and Transcripts</a></li><li><a href="https://eevis.codes/blog/2021-05-28/dont-develop-just-for-yourself-a-developers-checklist-to-accessibility/#wrapping-up"> Wrapping Up</a></li><li><a href="https://eevis.codes/blog/2021-05-28/dont-develop-just-for-yourself-a-developers-checklist-to-accessibility/#resources"> Resources</a></li>
</ol>
</nav>
<p>We, as developers, tend to develop sites unconsciously for people like ourselves. If we don't actively pay attention, the sites are often accessible only for certain types of people: Sighted mouse-users, who have good fine motor skills and are good at using computers. </p>
<p>It leads to moments where no one who navigates the web with only a keyboard (or keyboard simulating device) can access and interact with the site. It is a pretty big group of people!</p>
<p>We have a responsibility to make sites that work for all of our users. In some cases, it is required by law, but it is not the only reason we should care. </p>
<p>This blog post is aimed for those at the beginning of their accessibility journey. You might know some things, like "always provide an alt-text for an image," but are unsure what to write there. Or you don't have any idea how to know if your site is accessible at all.</p>
<p><strong>Following this blog post won't make your site 100% accessible!</strong> The goal of this post is to provide some information about some things to check manually after using some automated testing. </p>
<h2 id="keyboard-navigation">Keyboard Navigation</h2>
<p>First of all, abandon your mouse for a second. Don't use it. Try to navigate through the website using only the keyboard. Try to complete every task user needs to be able to complete on the website. Can you do it? Can you see where you are at the moment? Is the focus indicator (yes, that thing many designers and developers like to remove) visible enough? </p>
<p>If you can't use the whole website with only a keyboard, that is an accessibility and usability problem. However, one thing to note here is that keyboard interaction is not necessarily accomplished with just <kbd>tab</kbd> and <kbd>enter</kbd>, as it has been at some point. The general pattern goes, as <a href="https://www.w3.org/TR/wai-aria-practices-1.1/#kbd_generalnav">WAI-ARIA Authoring Practices</a> state:</p>
<blockquote>
<p>A primary keyboard navigation convention common across all platforms is that the <kbd>tab</kbd> and <kbd>shift</kbd>+<kbd>tab</kbd> keys move focus from one UI component to another while other keys, primarily the arrow keys, move focus inside of components that include multiple focusable elements.</p>
</blockquote>
<p>Another thing to consider in this point is focus indicators. They should be visible, and even if the default focus indicator on the browser is sufficient to pass the WCAG-requirements, it might be hard to see. So I would recommend enhancing the focus indicator to be more visible. Just remember that the color of the indicator should have a sufficient contrast ratio with the adjacent background.</p>
<h3 id="action-item-for-keyboard-navigation">Action Item for Keyboard Navigation</h3>
<p>Using only your keyboard, navigate through your site. Pay attention to navigation menus and other custom widgets that might lose focus because some elements are hidden incorrectly.</p>
<p><strong>Is everything reachable and operable without a mouse? Can you see the focus every time?</strong> </p>
<h2 id="alt-texts">Alt-Texts</h2>
<p>Another thing to check manually is alternative texts for the pictures. You might wonder, isn't it a thing automated testing catches? Well, yes. Automated testing tools crawl through the markup, and when they find an <code>img</code>-element, they check if the <code>alt</code>-attribute is present. They don't (and can't) check the quality of the alternative texts.</p>
<p>Not all images need alternative texts; however, they do need the <code>alt</code>-attribute. So for a purely decorative image, you need to provide an empty <code>alt</code>-attribute. This "hides" the image from the screen reader so that it won't get read at all. </p>
<p>You might ask, shouldn't screen reader users know about everything on the page? A person, who consumes the web by listening, might want to reduce the amount of information. </p>
<h3 id="action-item-for-alt-texts">Action Item for Alt-Texts</h3>
<p>Go through every image on the page. You can use, for example, <a href="https://chrispederick.com/work/web-developer/">Web Developer-extension</a> or the developer tools and check the image's alt attribute. </p>
<p><strong>Is the text descriptive? Or is the picture decorative?</strong> You can find more information on how you should write the alt-text for every type of image from <a href="https://webaim.org/techniques/alttext/">WebAIM: Alternative Text</a>.</p>
<h2 id="page-language">Page Language</h2>
<p>Another thing that automated tests catch, if it's missing, is the page language. Some of the project starters, such as <code>create-react-app,</code> have the lang-attribute set automatically, but some (I'm looking at you, NextJS) don't. </p>
<p>If the site's language is English, you're pretty much okay with the automatic language (if it exists). However, if it is not, then you need to change it. The reason is that the language screen readers use for the page comes from that <code>lang</code>-attribute. So if you have a page with content in Finnish, and there is that <code>en</code>-attribute, a screen reader would pronounce every word with an English accent. That's not beautiful. Here's an example with another way around. It's almost as bad:</p>
<video controls="">
<source src="https://videos.ctfassets.net/sk24pdnnlwhc/4cOy4EUnFHq4F1RMqL588x/22feb097de58d5c23c27aea967654fc8/page-language.mp4" type="video/mp4" />
</video>
<h3 id="action-item-for-page-language">Action Item for Page Language</h3>
<p>Check the language of the page in the page's <code>html</code>-attribute. You can do this from developer tools' Elements-tab. <strong>Is the language code the same as the primary language on the page?</strong></p>
<h2 id="text-resizing">Text Resizing</h2>
<p>Some people increase the text size of the webpage with, for example, browser settings. It means that the content takes more space and might flow differently. Developers should test if the site is working when text size has been increased to at least 200%. </p>
<h3 id="action-item-for-text-resizing">Action Item for Text Resizing</h3>
<p>Have the webpage on two browsers open, preferably side by side. In the first browser, use browser's built-in zooming for testing by using <kbd>CMD</kbd>+<kbd>+</kbd> if you're using a Mac, and <kbd>CTRL</kbd>+<kbd>+</kbd> if you're using Windows or Linux. Increase the size to the maximum, which should be 200%. Compare the zoomed site and the one without a zoom.</p>
<p><strong>Does your site still work? Is the text flowing okay? Does increasing the text size add horizontal scrolling? Is all the information still there?</strong></p>
<h2 id="color-alone">Color Alone</h2>
<p>If color alone conveys meaning, there are lots of users who miss out on that information. For example, suppose there is a list of different activities, and their level of advancement is communicated only with a colored box. In that case, there are some groups of people who can't access that information. </p>
<p>For example, a person who is color blind can't separate certain color combinations, and thus the information, when it's conveyed only with color, is not understandable. Also, for screen reader users, the color does not say anything; screen readers don't say anything about the web page's styles. </p>
<h3 id="action-item-for-color-alone">Action Item for Color Alone</h3>
<p>One thing that could be used as help for testing if the color alone is used to convey a meaning is to change the site into grayscale. </p>
<details>
<summary>How to turn a page grayscale?</summary>
You can turn a page grayscale from the developer tools in Chrome and Edge from the "Rendering"-tab. You can find it with the same instructions as for the [`prefers-reduced-motion`-simulation][3]. There is a header "Emulate vision deficiencies" on that tab, and you can choose Achromatopsia from the select.
</details>
<p><strong>Can you still understand all the information on the page?</strong></p>
<h2 id="mobile-zooming">Mobile Zooming</h2>
<p>I remember, that at some point it was customary to add this <code><meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" /></code> to disable the pinch-zoom on webpages. Nobody ever explained why it was, but it was everywhere I copied code to my projects. I didn't know then that it would make zooming on touch devices harder. </p>
<p>Some people need to use zoom to see the elements on the page. Also, many people want to, for example, zoom in on pictures to see how they look in that picture or some other detail. And sometimes, the website is unusable without zooming. That is a thing that would need fixing from the developers' side, but things don't always happen too fast, so meanwhile, users would need to use the zoom.</p>
<h3 id="action-item-for-mobile-zooming">Action Item for Mobile Zooming</h3>
<p>Open your site or app on a mobile device. Try zooming in with your fingers. <strong>Can you do it?</strong></p>
<h2 id="captions-and-transcripts">Captions and Transcripts</h2>
<h3 id="captions">Captions</h3>
<p>Imagine if you couldn't hear the words said in the video. How would you then understand what is going on in that video? It can happen for numerous reasons. You could have a hearing-related disability or be in a crowded place where you don't want to turn the sound on.</p>
<p>For these situations, captions are essential. Captions are a bit different from subtitles. They also contain information about important sounds on the video, and they identify the speaker if they're not easy to identify from the video. Captions can be either <em>closed</em>, meaning they can be turned on, or <em>open</em>, meaning they are always present. </p>
<p>And a word about auto-captions: Please, no. The speech recognition algorithms aren't that good yet, and they produce lousy quality. Some even call them "auto-craptions." Suppose you don't have a hearing-related disability and haven't encountered this in your life. In that case, I have another example that could maybe give some context: Services like Netflix and subtitles (when the subtitle language is not English). I mean, at least for Finnish, the subtitles are often just crap. The reason is either using automated translations or not paying enough for professionals to do the translations.</p>
<h3 id="transcripts">Transcripts</h3>
<p>Transcripts are a way for DeafBlind users to understand what's going on in videos or audio content (say, podcasts). Also, for audio-only content, transcripts are the only way for Deaf people to get the message. </p>
<p>If you're interested to learn more about captions, transcripts, and audio descriptions (which I'm not covering here), <a href="https://webaim.org/techniques/captions/">WebAIM: Captions, Transcripts, and Audio Descriptions</a> is a good resource on that.</p>
<h3 id="action-item-for-captions-and-transcripts">Action Item for Captions and Transcripts</h3>
<p>Go through every piece of multimedia on your website. <strong>Are the captions and transcripts in place (depending on the media type)?</strong></p>
<h2 id="wrapping-up">Wrapping Up</h2>
<p>In this blog post, I have introduced seven checks any developer can do to ensure that their website is a bit more accessible. Some of them take more time than others, but they're worth it!</p>
<p>Do you have any tips for simple checks for accessibility problems that aren't easy to catch with automated tests?</p>
<h2 id="resources">Resources</h2>
<ul>
<li><a href="https://www.w3.org/TR/wai-aria-practices-1.1/#kbd_generalnav">WAI-ARIA Authoring Practices</a></li>
<li><a href="https://webaim.org/techniques/alttext/">WebAIM: Alternative Text</a></li>
<li><a href="https://webaim.org/techniques/captions/">WebAIM: Captions, Transcripts, and Audio Descriptions</a></li>
<li><a href="https://www.youtube.com/watch?v=wIj-NymT5fY"><code>prefers-reduced-motion</code>-simulation</a></li>
<li><a href="https://chrispederick.com/work/web-developer/">Web Developer-extension</a></li>
</ul>
<p><em>Cover photo by <a href="https://unsplash.com/@glenncarstenspeters?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Glenn Carstens-Peters</a> on <a href="https://unsplash.com/?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a></em></p>
</article>
Why I'm Not One of the Guys2021-07-24T00:00:00.000Zhttps://eevis.codes/blog/2021-07-24/why-im-not-one-of-the-guys/
<nav id="breadcrumbs" aria-label="breadcrumbs">
<ol>
<li>
<a href="https://eevis.codes/blog/">Blog</a>
<span aria-hidden="true" class="breadcrumb-separator">/<span>
</span></span></li>
<li>Why I'm Not One of the Guys</li><li>
</li></ol>
</nav>
<article class="blogPostLong">
<h1 id="/2021-07-24/why-im-not-one-of-the-guys">Why I'm Not One of the Guys</h1>
<div class="reading-time-and-date">
<p>
Reading time: about 7 minutes
</p>
<span class="separator"></span>
<p>Published <time datetime="24th Jul, 2021">24th Jul, 2021</time></p>
</div>
<section class="likes-and-boosts">
<div>
<svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="boosts-title">
<title id="boosts-title">Boosts</title>
<path d="M7.19 4.35047C5.15 6.64047 3.75 9.93047 3.62 10.2405L0 8.69047L4.05 4.64047C4.52 4.17047 5.2 3.96047 5.86 4.09047L7.19 4.35047ZM9.17 15.0005C9.17 15.0005 12.91 13.4505 15.06 11.3005C20.46 5.90047 19.56 1.68047 19.27 0.730473C18.32 0.430473 14.1 -0.459527 8.7 4.94047C6.55 7.09047 5 10.8305 5 10.8305L9.17 15.0005ZM15.65 12.8105C13.36 14.8505 10.07 16.2505 9.76 16.3805L11.31 20.0005L15.36 15.9505C15.83 15.4805 16.04 14.8005 15.91 14.1405L15.65 12.8105ZM7 16.0005C7 16.8305 6.66 17.5805 6.12 18.1205C4.94 19.3005 0 20.0005 0 20.0005C0 20.0005 0.7 15.0605 1.88 13.8805C2.2988 13.4595 2.83325 13.1724 3.4155 13.0558C3.99775 12.9392 4.60153 12.9982 5.15017 13.2253C5.69882 13.4525 6.16758 13.8376 6.49694 14.3317C6.8263 14.8258 7.0014 15.4067 7 16.0005ZM11 7.00047C11 5.90047 11.9 5.00047 13 5.00047C14.1 5.00047 15 5.90047 15 7.00047C15 8.10047 14.1 9.00047 13 9.00047C11.9 9.00047 11 8.10047 11 7.00047Z" fill="#00696D"></path>
</svg>
0
</div>
<div>
<svg role="img" width="20" height="19" viewBox="0 0 20 19" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="likes-title">
<title id="likes-title">Likes</title>
<path d="M10 18.35L8.55 17.03C3.4 12.36 0 9.27 0 5.5C0 2.41 2.42 0 5.5 0C7.24 0 8.91 0.81 10 2.08C11.09 0.81 12.76 0 14.5 0C17.58 0 20 2.41 20 5.5C20 9.27 16.6 12.36 11.45 17.03L10 18.35Z"></path>
</svg>
0
</div>
</section>
<img class="cover-photo" src="https://images.ctfassets.net/mpqufjsy02zr/3QKpt2Hjkbsrcxj5rTNLio/001a1f82eb12578e613ebcd0e1101825/etienne-girardet-j1YEM-Fq9xM-unsplash.jpg" alt="Word Thanks sprayed with red on white background. " />
<div class="blog-tags">
<a href="https://eevis.codes/tags/career"><span class="blog-tag purple">career</span></a> <a href="https://eevis.codes/tags/general"><span class="blog-tag turquoise">general</span></a>
</div>
<nav aria-label="Why I'm Not One of the Guys Table of Contents" class="table-of-contents">
<h2>Table of Contents</h2>
<ol>
<li><a href="https://eevis.codes/blog/2021-07-24/why-im-not-one-of-the-guys/#the-problem-of-man-default"> The Problem of Man-Default</a></li><li><a href="https://eevis.codes/blog/2021-07-24/why-im-not-one-of-the-guys/#guys-is-not-gender-neutral"> Guys is Not Gender Neutral</a></li><li><a href="https://eevis.codes/blog/2021-07-24/why-im-not-one-of-the-guys/#well-why-dont-i-just-speak-up"> Well, Why Don't I Just Speak Up?</a></li><li><a href="https://eevis.codes/blog/2021-07-24/why-im-not-one-of-the-guys/#what-can-i-use-then"> What Can I Use, Then?</a></li><li><a href="https://eevis.codes/blog/2021-07-24/why-im-not-one-of-the-guys/#wrapping-up"> Wrapping Up</a></li>
</ol>
</nav>
<p><em>Spanish translation by <a href="https://www.ibidem-translations.com/">Ibidem</a>: <a href="https://www.ibidem-translations.com/edu/traduccion-lenguaje-no-sexista/">Por qué no soy “uno de los chicos”
</a></em></p>
<p>"Hey, guys" is a phrase I often hear. It usually continues with words like "what do you think about this?" And if the context is within a mixed-gender group, I feel left out. I feel like they're talking to everyone else (or, at least, all who identify as a man) but me. </p>
<p>I know many native English speakers use the phrase as gender-neutral.
But it is not, and in this blog post, I will discuss the problems of using that expression. I will also address some other aspects of the "man-default." One of the things it means is, for example, that if we are talking about a developer, that developer is often referred to as "he" (so, "A developer found a bug. He started fixing it").</p>
<p>This is a very personal topic for me, as I've had to fight for my spot as a developer, and I'm still facing these assumptions that I'm less of a professional because of my gender. So if you don't recognize the problem, please just believe me and don't start mansplaining how, for example, "you guys" is a gender-neutral term, and I should just suck it up. Please just don't. </p>
<h2 id="the-problem-of-man-default">The Problem of Man-Default</h2>
<p>When you refer to a hypothetical person whose gender is unknown, what is the pronoun you use? If the answer is "he," then congrats, you've found the man-default. It means that you default to man when talking about someone whose gender you don't know. </p>
<p>Man-default can also be found in expressions such as "manning the station," "man-hours," or "chairman." It is visible in other languages as well. For example, in Finnish, despite having only one personal pronoun for all genders, we have words such as "lakimies" (=lawyer, literal translation would be "law man"), "palomies" (fireman), or "miehittää" (same as in "manning the station"). </p>
<p>Another example of man-default comes from sports. Often men's league is referred to as "the league," and then there is the women's league with the word "women" in it. For example, we have the NBA (National Basketball Association) and WNBA (Women's National Basketball Association). It displays men as default and women as "other," something that needs an explanation. </p>
<p>But you know, the language we use shapes the reality around us. If we always speak about men doing something and especially default to men when talking about fields where men are the majority, we maintain those structures and keep the image up. </p>
<p>Ok, this might sound a bit abstract, but let me try to explain it a bit further: If we always refer to software developers as "he," we (unconsciously) build an image where all software developers are men. It leads to situations where men are seen as better developers, because hey, of course. And then women are seen as less of professionals because they aren't <em>men</em> who are the default. And this is how unconscious bias, which leads to discrimination, builds. </p>
<p>It is also a topic that has become visible with translation algorithms. Nicolas Kayser-Bril wrote <a href="https://algorithmwatch.org/en/google-translate-gender-bias/">a piece for Algorithm Watch</a> about how Google Translate systematically changes the gender for translations when the initial gender doesn't fit the stereotypes. </p>
<p>There was also an interesting <a href="https://twitter.com/vuokko/status/1369185483683733505?s=20">Twitter-thread about Google Translate and pronouns</a> during the International Women's Day in 2021: </p>
<p><a href="https://twitter.com/vuokko/status/1369185483683733505?s=20">https://twitter.com/vuokko/status/1369185483683733505?s=20</a></p>
<p>These examples provide some fascinating insight into how language shapes reality. These translations are a direct consequence of the data used to train these algorithms. We, humans, are the same - if the data we get defaults always to, for example, men being the developers, we tend to believe that being a developer is meant only for men.</p>
<h2 id="guys-is-not-gender-neutral">Guys is Not Gender Neutral</h2>
<p>Back to the term "guys." Some argue that it is a gender-neutral term. Well, it is not. It originates from the word "guy," which is singular and means a man. Yes, I know, you might have learned it and been using it believing it is gender-neutral. That is ok - you didn't know. You are not a bad person. But could you please remember from now on that there are people who feel excluded when someone uses the word? Thank you!</p>
<p>Sara Bent from Hotjar writes about how they had a <a href="https://www.hotjar.com/blog/gender-inclusive-language-workplace/">"Guys Jar"-challenge</a>, a version of a swear jar. In their version, every time someone participating used the word 'guys' to refer to a group of mixed (or only-women) people, they had to pay. </p>
<p>I like how Sara puts the idea of using "guys" (from the blog post linked above):</p>
<blockquote>
<p>Even though most people who use the term don't do so with the intent of it being sexist or exclusive of women, <strong>it can and often does cause women to feel left out of the conversation.</strong> Imagine you used 'gals' to refer to a room full of men and women—do you think the men would respond?</p>
</blockquote>
<p>I've had and read many conversations with people (often with cis-men) about why they think "guys" is a gender-neutral term. One of the "best" arguments I've heard so far is that "we can't change the way soccer-moms in suburbs in the States use this term, so using it is ok for us as well." Well yeah, one thing is correct; we probably can't change that. </p>
<p>But you know, the conversation where they said this wasn't anywhere near those soccer moms - it was in Finland, in a professional context where English is the language used. And in a group where someone had just said they feel excluded when others use the word "guys" when referring to them. It was about the language used in the said setting. That felt... absurd.</p>
<h2 id="well-why-dont-i-just-speak-up">Well, Why Don't I Just Speak Up?</h2>
<p>I don't know about you, but when I'm in the minority, and I've seen that raising these issues sometimes gets even aggressive responses, it is hard to speak up. </p>
<p>Another thing that makes speaking up hard is that when I do, people usually assume I'm criticizing them, not their actions. These people tend to feel insulted, and you know how people are when they feel humiliated. The focus shifts to <em>their</em> feelings and me trying to tell them that no, I'm not assuming you're a terrible person. I'm only trying to bring to the attention things that you probably unconsciously are doing. </p>
<p>I've had good examples of when people have listened when I've brought this issue to attention. Still, more often, it ends up in an endless conversation about soccer moms and other non-relevant arguments. It gets tiring very soon.</p>
<p>And you know... It shouldn't be the responsibility of the minority to try to fix these issues alone. If it's something that has been brought up in a community before, the majority has the responsibility to act. </p>
<h2 id="what-can-i-use-then">What Can I Use, Then?</h2>
<p>So, you've decided to change your vocabulary. Thank you! I really appreciate that! You might be wondering what to use then. Here are some alternatives and considerations.</p>
<h3 id="alternatives-for-guys">Alternatives for Guys</h3>
<p>There are multiple alternatives you can use instead of "hey guys." Here are some examples with <a href="https://twitter.com/krees/status/1106617681116045312?s=20">Kim Rees' tweet</a> about some options:</p>
<p><a href="https://twitter.com/krees/status/1106617681116045312?s=20">https://twitter.com/krees/status/1106617681116045312?s=20</a></p>
<h3 id="using-they-instead-of-he-or-she-for-that-matter">Using They Instead of He (or She for That Matter)</h3>
<p>When talking about a person whose gender you don't know - wheater a real person, or a hypothetical person (such as a hypothetical developer), default to "they"-pronoun. </p>
<p>It might feel a bit weird at first, but I promise you, you'll get used to it. I speak from experience :) </p>
<h3 id="being-aware-of-man-default">Being Aware of Man-Default</h3>
<p>One more thing I want to point out is being aware of the man-default. Start paying attention to the words you use; do they keep up the picture of a man being the default? Yes, it's a challenging task to do because we use language unconsciously and automatically. But you can try anyway.</p>
<p>There are usually gender-neutral words to use instead of the ones defaulting to man. Here are some examples:</p>
<ul>
<li>Fireman -> Firefighter</li>
<li>Chairman -> Chairperson</li>
<li>Freshman -> First-year student</li>
</ul>
<p>And so on. The internet is full of examples, so go and educate yourself!</p>
<h2 id="wrapping-up">Wrapping Up</h2>
<p>So, TLDR; "Hey guys" is not a gender-neutral expression, and there are lots of people who feel excluded when someone uses it to refer to a group where they belong. I'm one of them. </p>
<p>There are better alternatives for the expression, and we should use them. Also, using "he" as a default pronoun is problematic, so let's use "they." And let's be aware of the fact that man is seen as a default in many places, so let's pay attention to the words we use.</p>
<p>You can find more examples of non-sexist language in <a href="https://geekfeminism.wikia.org/wiki/Nonsexist_language">Geek Feminism Wiki's guide on non-sexist language</a>. </p>
<p><em>Cover photo by <a href="https://unsplash.com/@etiennegirardet?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Etienne Girardet</a> on <a href="https://unsplash.com/?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a>.</em></p>
</article>
The Dark Side of Blogging2021-09-09T00:00:00.000Zhttps://eevis.codes/blog/2021-09-09/the-dark-side-of-blogging/
<nav id="breadcrumbs" aria-label="breadcrumbs">
<ol>
<li>
<a href="https://eevis.codes/blog/">Blog</a>
<span aria-hidden="true" class="breadcrumb-separator">/<span>
</span></span></li>
<li>The Dark Side of Blogging</li><li>
</li></ol>
</nav>
<article class="blogPostLong">
<h1 id="/2021-09-09/the-dark-side-of-blogging">The Dark Side of Blogging</h1>
<div class="reading-time-and-date">
<p>
Reading time: about 6 minutes
</p>
<span class="separator"></span>
<p>Published <time datetime="9th Sep, 2021">9th Sep, 2021</time></p>
</div>
<section class="likes-and-boosts">
<div>
<svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="boosts-title">
<title id="boosts-title">Boosts</title>
<path d="M7.19 4.35047C5.15 6.64047 3.75 9.93047 3.62 10.2405L0 8.69047L4.05 4.64047C4.52 4.17047 5.2 3.96047 5.86 4.09047L7.19 4.35047ZM9.17 15.0005C9.17 15.0005 12.91 13.4505 15.06 11.3005C20.46 5.90047 19.56 1.68047 19.27 0.730473C18.32 0.430473 14.1 -0.459527 8.7 4.94047C6.55 7.09047 5 10.8305 5 10.8305L9.17 15.0005ZM15.65 12.8105C13.36 14.8505 10.07 16.2505 9.76 16.3805L11.31 20.0005L15.36 15.9505C15.83 15.4805 16.04 14.8005 15.91 14.1405L15.65 12.8105ZM7 16.0005C7 16.8305 6.66 17.5805 6.12 18.1205C4.94 19.3005 0 20.0005 0 20.0005C0 20.0005 0.7 15.0605 1.88 13.8805C2.2988 13.4595 2.83325 13.1724 3.4155 13.0558C3.99775 12.9392 4.60153 12.9982 5.15017 13.2253C5.69882 13.4525 6.16758 13.8376 6.49694 14.3317C6.8263 14.8258 7.0014 15.4067 7 16.0005ZM11 7.00047C11 5.90047 11.9 5.00047 13 5.00047C14.1 5.00047 15 5.90047 15 7.00047C15 8.10047 14.1 9.00047 13 9.00047C11.9 9.00047 11 8.10047 11 7.00047Z" fill="#00696D"></path>
</svg>
0
</div>
<div>
<svg role="img" width="20" height="19" viewBox="0 0 20 19" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="likes-title">
<title id="likes-title">Likes</title>
<path d="M10 18.35L8.55 17.03C3.4 12.36 0 9.27 0 5.5C0 2.41 2.42 0 5.5 0C7.24 0 8.91 0.81 10 2.08C11.09 0.81 12.76 0 14.5 0C17.58 0 20 2.41 20 5.5C20 9.27 16.6 12.36 11.45 17.03L10 18.35Z"></path>
</svg>
0
</div>
</section>
<img class="cover-photo" src="https://images.ctfassets.net/mpqufjsy02zr/65Xvi26ULl2ll2BRwvFmuk/9dce47b5a8dfdad9510f9ba310ade4c7/marco-bianchetti-jkfxmKswsXY-unsplash.jpg" alt="Two lamps shining dimly, and behind them there is a dark sky. " />
<div class="blog-tags">
<a href="https://eevis.codes/tags/career"><span class="blog-tag purple">career</span></a> <a href="https://eevis.codes/tags/general"><span class="blog-tag turquoise">general</span></a>
</div>
<nav aria-label="The Dark Side of Blogging Table of Contents" class="table-of-contents">
<h2>Table of Contents</h2>
<ol>
<li><a href="https://eevis.codes/blog/2021-09-09/the-dark-side-of-blogging/#some-background"> Some Background</a></li><li><a href="https://eevis.codes/blog/2021-09-09/the-dark-side-of-blogging/#writing-quality-content-takes-time"> Writing Quality Content Takes Time</a></li><li><a href="https://eevis.codes/blog/2021-09-09/the-dark-side-of-blogging/#the-pressure"> The Pressure</a></li><li><a href="https://eevis.codes/blog/2021-09-09/the-dark-side-of-blogging/#responses-to-controversial-topics"> Responses to Controversial Topics</a></li><li><a href="https://eevis.codes/blog/2021-09-09/the-dark-side-of-blogging/#summing-up-the-tips"> Summing Up The Tips</a></li>
</ol>
</nav>
<p>Lately, I've seen multiple excellent posts about why developers should blog. They list very good reasons, and I wholeheartedly agree with them. Blogging is good for many things, it's fun, and you learn a lot during the process. </p>
<p>However, there are downsides to blogging as well, especially if you write about controversial topics. This blog post will discuss some of these downsides, tell my own experiences, and share tips on conquering those not-so-great things. I'm writing the tips for myself as advice I would have needed to hear, so they might not all apply to you, but I hope you'll get at least something out of this post!</p>
<h2 id="some-background">Some Background</h2>
<p>I started actively blogging about a year ago. My motivator was, at first, to write blog posts 16 weeks in a row to get the 16-weeks badge from <a href="https://dev.to/">Dev.to</a>. I earned the badge, and if you're interested, I wrote <a href="https://dev.to/eevajonnapanula/checkpoint-16-weeks-of-blogging-2ofj">a blog post about what I learned from that journey.</a></p>
<p>Blogging is something I like a lot. It has given me a way to construct my thoughts through writing and learn in the process. I already have a good archive of articles I've written. It hasn't been once or twice that I've had the opportunity to refer to one of my blog posts in a conversation.</p>
<p>However, as much I like this, there have been downsides as well. And during the past year, I've been struggling with stuff - as I would imagine most of us have because of Covid and all the isolation it causes. I'm also recovering from a brain injury, which makes me prone to fatigue. So these things have significantly slowed my speed of writing, especially in 2021. </p>
<p>So, let's have a look at the downsides and some advice!</p>
<h2 id="writing-quality-content-takes-time">Writing Quality Content Takes Time</h2>
<p>If you want to write some quality content, it takes time. Suppose you're creating a tutorial of some sort, building the end result, testing it, and writing the blog post. It can take a long time from the start to publishing it. </p>
<p>In Dev, I've come across multiple posts that are basically just (short) lists of links. There is time and place for those lists, but at least I like to read longer posts with some actual content, and from the discussions I've seen in Dev, I'm not the only one. And writing longer posts takes more time. </p>
<p>Editing is another part of the writing process that requires time. Of course, it is possible to just write and then publish. But parts of writing <em>quality</em> content are editing, proofreading, and all those moments when you might need to delete half of your blog post and start over. </p>
<p>So, my advice for this would be to <strong>give yourself time</strong>. In life, all kinds of things can happen. It is okay if you don't write every week, especially if you're writing for yourself and not, for example, as a job. And when you do have time and energy, purposely <strong>book time for writing, editing, and all that</strong>. </p>
<h2 id="the-pressure">The Pressure</h2>
<p>You also might feel pressured to write and publish. For some, this is not an issue, but at least for me, it has been. When I was blogging for 16 weeks, publishing a blog post a week, I often felt pressure in the back of my mind. </p>
<p>Okay, it was me wanting to keep the streak going, and nobody else was asking me to do anything. Nevertheless, it really stressed me out sometimes. After the 16 weeks passed and I got my Dev-badge, the publishing pace slowed significantly down until, in the summer, I've published maybe a couple of posts. Well, depending on how you define "summer."</p>
<p>It's also possible that the pressure comes from outside; if you write for a company or a commissioned piece, then it's not just you anymore. There are deadlines, and someone else is depending on you. </p>
<p>As mentioned, there was no one else pressuring me on this, at least on purpose. I'm the one who makes me feel like I need to write. Sometimes I don't even know why; there is just that nagging feeling that I haven't published anything in ages. </p>
<p>There is another kind of pressure as well: writing about specific topics. I mean, my blog posts have been mostly about accessibility topics, but there have been some other themes as well. Still, sometimes I feel like I need to "stay in my lane." Now that I think of it, I've actually been told that when writing about equality-related topics. But I think there's richness in being able to explore different themes.</p>
<p>So what kind of advice would I give to combat the pressure of writing? First of all, <strong>be merciful and compassionate to yourself</strong>. No one's life is depending on your writing, and it is okay to take your time. And <strong>it is also okay to branch out and write about other topics as well</strong>. These might feel like a bit obvious pieces of advice, but at least I need to hear them once in a while. </p>
<h2 id="responses-to-controversial-topics">Responses to Controversial Topics</h2>
<p>As I mentioned, I've been mostly writing about accessibility and front-end development. Once in a while, I've thrown in some posts related to controversial topics. Good examples of these are language and being a woman in an industry where women haven't had space for a long time. </p>
<p>Recently I wrote about why I don't want to be referred to with the phrase "you guys." First, I shared it on LinkedIn, and the response I got was pretty much supportive, with a couple of not-so-supportive comments. I thought, okay, "let's publish this on Dev." I wasn't prepared for the flood of comments it received.</p>
<p>In the comments, there were some encouraging comments, and then some comments with good critique and conversation. But then the negative comments started coming, and I felt paralyzed. I wanted to answer the ones with encouragement and good commentary, but the negativity just drained me. So I want to apologize to anyone who was waiting for my answer and never got it! I had to draw a line to protect my mental health.</p>
<p>Some of the negative comments were clear trolls; some of them clearly tried, but it felt like they hadn't read anything more but the title of the blog post. And the mansplaining. Oh, the mansplaining! For those who are offended by the word, I do not mean that all opposing views were mansplaining. As said, some good comments challenged my points, and they definitely don't fall under the term mansplaining. But then again, some comments definitely were mansplaining. </p>
<p>As said, those comments drained me. I was contemplating removing the whole post from Dev, but in the end, two things kept me from deleting it. First was all the encouraging comments I received in the post, and the second was the support I got from the Dev's team (Thank you again, Michael, for reaching out! It meant a lot!).</p>
<p>So what I learned from this, and what tips could I give? First, suppose you write about controversial topics and receive a similar reception. In that case, <strong>it is totally okay not to answer the comments</strong>. You and your health come first. And <strong>it's okay to draw lines and keep them</strong>. You don't owe anything to anyone. This is especially true for people from a minority, as they tend to get more trolling and negative comments just because of their background. </p>
<h2 id="summing-up-the-tips">Summing Up The Tips</h2>
<p>So, to sum up, what I've been discussing, here are the tips from the previous sections:</p>
<ol>
<li>Give yourself time. </li>
<li>Book time for the writing process.</li>
<li>Be merciful to yourself. </li>
<li>It's okay not to answer all comments.</li>
<li>Draw lines, and keep them.</li>
</ol>
<p><em>Cover photo by <a href="https://unsplash.com/@marcobian?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Marco Bianchetti</a> on <a href="https://unsplash.com/">Unsplash</a></em></p>
</article>
Making Better Pull Requests2021-10-18T00:00:00.000Zhttps://eevis.codes/blog/2021-10-18/making-better-pull-requests/
<nav id="breadcrumbs" aria-label="breadcrumbs">
<ol>
<li>
<a href="https://eevis.codes/blog/">Blog</a>
<span aria-hidden="true" class="breadcrumb-separator">/<span>
</span></span></li>
<li>Making Better Pull Requests</li><li>
</li></ol>
</nav>
<article class="blogPostLong">
<h1 id="/2021-10-18/making-better-pull-requests">Making Better Pull Requests</h1>
<div class="reading-time-and-date">
<p>
Reading time: about 5 minutes
</p>
<span class="separator"></span>
<p>Published <time datetime="18th Oct, 2021">18th Oct, 2021</time></p>
</div>
<section class="likes-and-boosts">
<div>
<svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="boosts-title">
<title id="boosts-title">Boosts</title>
<path d="M7.19 4.35047C5.15 6.64047 3.75 9.93047 3.62 10.2405L0 8.69047L4.05 4.64047C4.52 4.17047 5.2 3.96047 5.86 4.09047L7.19 4.35047ZM9.17 15.0005C9.17 15.0005 12.91 13.4505 15.06 11.3005C20.46 5.90047 19.56 1.68047 19.27 0.730473C18.32 0.430473 14.1 -0.459527 8.7 4.94047C6.55 7.09047 5 10.8305 5 10.8305L9.17 15.0005ZM15.65 12.8105C13.36 14.8505 10.07 16.2505 9.76 16.3805L11.31 20.0005L15.36 15.9505C15.83 15.4805 16.04 14.8005 15.91 14.1405L15.65 12.8105ZM7 16.0005C7 16.8305 6.66 17.5805 6.12 18.1205C4.94 19.3005 0 20.0005 0 20.0005C0 20.0005 0.7 15.0605 1.88 13.8805C2.2988 13.4595 2.83325 13.1724 3.4155 13.0558C3.99775 12.9392 4.60153 12.9982 5.15017 13.2253C5.69882 13.4525 6.16758 13.8376 6.49694 14.3317C6.8263 14.8258 7.0014 15.4067 7 16.0005ZM11 7.00047C11 5.90047 11.9 5.00047 13 5.00047C14.1 5.00047 15 5.90047 15 7.00047C15 8.10047 14.1 9.00047 13 9.00047C11.9 9.00047 11 8.10047 11 7.00047Z" fill="#00696D"></path>
</svg>
0
</div>
<div>
<svg role="img" width="20" height="19" viewBox="0 0 20 19" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="likes-title">
<title id="likes-title">Likes</title>
<path d="M10 18.35L8.55 17.03C3.4 12.36 0 9.27 0 5.5C0 2.41 2.42 0 5.5 0C7.24 0 8.91 0.81 10 2.08C11.09 0.81 12.76 0 14.5 0C17.58 0 20 2.41 20 5.5C20 9.27 16.6 12.36 11.45 17.03L10 18.35Z"></path>
</svg>
0
</div>
</section>
<img class="cover-photo" src="https://images.ctfassets.net/mpqufjsy02zr/3dXHS6tm3G1FFRTnarWKfn/c681f33b819c1768fa4324aa92b4970d/thisisengineering-raeng-iQqRM0XJvn8-unsplash.jpg" alt="Woman working on a computer. She has red, long hair, and coding editor open in her external screen. " />
<div class="blog-tags">
<a href="https://eevis.codes/tags/general"><span class="blog-tag turquoise">general</span></a> <a href="https://eevis.codes/tags/hacktoberfest"><span class="blog-tag blue">hacktoberfest</span></a>
</div>
<nav aria-label="Making Better Pull Requests Table of Contents" class="table-of-contents">
<h2>Table of Contents</h2>
<ol>
<li><a href="https://eevis.codes/blog/2021-10-18/making-better-pull-requests/#keep-the-pull-request-in-scope"> Keep the Pull Request in Scope</a></li><li><a href="https://eevis.codes/blog/2021-10-18/making-better-pull-requests/#give-context-to-the-reviewer"> Give Context to the Reviewer</a></li><li><a href="https://eevis.codes/blog/2021-10-18/making-better-pull-requests/#add-a-picture-of-the-changes"> Add a Picture of the Changes</a></li><li><a href="https://eevis.codes/blog/2021-10-18/making-better-pull-requests/#pull-request-is-a-place-for-discussion"> Pull Request is a Place for Discussion</a></li><li><a href="https://eevis.codes/blog/2021-10-18/making-better-pull-requests/#summing-up"> Summing Up</a></li><li><a href="https://eevis.codes/blog/2021-10-18/making-better-pull-requests/#links"> Links </a></li>
</ol>
</nav>
<p>Hactoberfest is here! Time to find those issues and participating repositories, write some code, and create pull requests! I've been eagerly waiting for October, mostly because of Hacktoberfest. </p>
<p>Last year, I participated in Hacktoberfest by being a contributor and a maintainer. If you're interested, you can read about my blog post about last year's experiences:
<a href="https://dev.to/eevajonnapanula/story-of-an-accidental-open-source-maintainer-6jh">Eevis Panula - Story of an accidental open source maintainer</a></p>
<p>This year, I decided to take it a bit easier. I mean, as mentioned in the blog post, last year we bought a house and were moving during October. Another challenging thing from last year was the maintainer part. I loved it, but it really, really stressed me out. So I'm not going to do it this year. And there's another piece to the puzzle this year as well: I'm switching jobs, so that's another reason to take it easy during this Hacktoberfest.</p>
<p>One big part of Hacktoberfest is creating pull requests. I remember the first time I made a PR in an open-source repository. I didn't know the repository's maintainers, and it didn't have any guidelines for pull requests, such as a pull request template. I had no idea what the maintainer expected from a PR. I felt super anxious. </p>
<p>If I had known back then what I know now, it would've been easier for me. So that's why in this blog post, I'm sharing some tips on how to make better PRs. These are the best practices I've picked up along the way. However, note that there might be some other conventions in the project you're contributing. Be sure to check them. There might be a <a href="https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/about-issue-and-pull-request-templates">pull request template</a> or contribution guidelines. </p>
<p>The tips I'm going to give are the following:</p>
<ul>
<li><a href="https://eevis.codes/blog/2021-10-18/making-better-pull-requests/#keep-the-pull-request-in-scope">Keep the pull request in scope</a></li>
<li><a href="https://eevis.codes/blog/2021-10-18/making-better-pull-requests/#give-context-to-the-reviewer">Give context to the reviewer</a></li>
<li><a href="https://eevis.codes/blog/2021-10-18/making-better-pull-requests/#add-a-picture-of-the-changes">Add a picture of the changes</a></li>
<li><a href="https://eevis.codes/blog/2021-10-18/making-better-pull-requests/#pull-request-is-a-place-for-discussion">Pull request is a place for discussion</a></li>
</ul>
<h2 id="keep-the-pull-request-in-scope">Keep the Pull Request in Scope</h2>
<p>One reason for keeping the PR in scope is that smaller PRs are always easier to review than those with lots of changes. Sometimes, of course, the feature you are working on is a big one, and thus the PR gets enormous. However, keeping in the scope in those cases means that if you find things to fix or improve, I would suggest creating a separate PR if they're not part of the feature. </p>
<p>Sometimes it would be good to separate the feature into smaller PRs. The scope gets smaller, and thus there will be less code to review. In these cases, I would advise splitting the PR into logical parts. For example, if you are building an app for period tracking, and the feature you're implementing is a calendar view where the user adds their data, there are multiple ways to split that. </p>
<p>Maybe the first part would be creating the endpoint to the backend and then adding a simple way to input the data with just input fields (so, no calendar yet). Maybe you could combine these all into a feature branch in the end. </p>
<p>Whatever you decide to do, be sure to check what the conventions are in the project you're working on. If you're working with a team, you can reach out and talk with them. There might also be a document about contributing, where there might be something about this topic.</p>
<h2 id="give-context-to-the-reviewer">Give Context to the Reviewer</h2>
<p>When I'm reviewing a pull request, I love seeing some context about the changes. It might be a link to an issue (with more info than a vague title), or it might be a text explaining the context in the PR description. Anyway, having that context helps me get into reviewing mode and know what I'm reviewing.</p>
<p>Continuing from the example from the previous section, I would briefly write about the calendar view as a goal or link to the issue and then explain what I did in the pull request. I would also add some notes about the decision to split the feature and maybe some words about what's not yet included.</p>
<h2 id="add-a-picture-of-the-changes">Add a Picture of the Changes</h2>
<p>When the pull request is about changing something in the UI, I love seeing a picture of the changes. It's part of the context, and I know what to look for when I pull the branch and test it. </p>
<p>Continuing with the example, after moving on to making changes to the UI, I would add screenshots of those changes to the pull request. I might consider adding a before picture when finally implementing the calendar view to see how the feature evolves. That, however, would depend on the situation. </p>
<h2 id="pull-request-is-a-place-for-discussion">Pull Request is a Place for Discussion</h2>
<p>One piece of advice I want to give here is that pull requests are places for discussion. It's ok to submit a pull request with questions (you could use a draft pull request in Github for that). On the other hand, you'll likely get questions and comments. Maybe even request to change something. When you do, and if they're not clear, it's always ok to ask for clarification or the reasons for change requests. </p>
<p>Why am I pointing this out? Well, at the beginning of my career, I felt like all the things said were God's truth, and I, as a newbie developer, didn't have any place to ask or question anything. That was even if the reviewers were friendly and tried to facilitate an atmosphere where that would've been ok. I would've needed to hear this advice many more times back then for it to sink in. </p>
<h2 id="summing-up">Summing Up</h2>
<p>In this blog post, I've discussed pull requests and how to make them better. The advice is subjective, and it's based on my own experiences, and you might have other opinions. That's ok. </p>
<p>I pointed out that it would be good to keep the pull request in scope and try not to do everything in one pull request. If you want to make some additional changes, I'd suggest opening a separate PR for that. Also, in the pull request description, give context about the pull request for the reviewer. Additionally, if there are changes in the UI, I suggest adding a screenshot of those changes. The last thing I pointed out is that pull requests are places for discussion.</p>
<h2 id="links">Links</h2>
<ul>
<li><a href="https://dev.to/eevajonnapanula/story-of-an-accidental-open-source-maintainer-6jh">Eevis Panula - Story of an accidental open source maintainer</a></li>
<li><a href="https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/about-issue-and-pull-request-templates">Github - Pull request template</a></li>
</ul>
<p><em>Cover photo by <a href="https://unsplash.com/@thisisengineering?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">ThisisEngineering RAEng</a> on <a href="https://unsplash.com/@thisisengineering?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a></em></p>
</article>
Thoughts From Reading Demystifying Disability by Emily Ladau2021-11-06T00:00:00.000Zhttps://eevis.codes/blog/2021-11-06/thoughts-from-reading-demystifying-disability-by-emily-ladau/
<nav id="breadcrumbs" aria-label="breadcrumbs">
<ol>
<li>
<a href="https://eevis.codes/blog/">Blog</a>
<span aria-hidden="true" class="breadcrumb-separator">/<span>
</span></span></li>
<li>Thoughts From Reading Demystifying Disability by Emily Ladau</li><li>
</li></ol>
</nav>
<article class="blogPostLong">
<h1 id="/2021-11-06/thoughts-from-reading-demystifying-disability-by-emily-ladau">Thoughts From Reading Demystifying Disability by Emily Ladau</h1>
<div class="reading-time-and-date">
<p>
Reading time: about 5 minutes
</p>
<span class="separator"></span>
<p>Published <time datetime="6th Nov, 2021">6th Nov, 2021</time></p>
</div>
<section class="likes-and-boosts">
<div>
<svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="boosts-title">
<title id="boosts-title">Boosts</title>
<path d="M7.19 4.35047C5.15 6.64047 3.75 9.93047 3.62 10.2405L0 8.69047L4.05 4.64047C4.52 4.17047 5.2 3.96047 5.86 4.09047L7.19 4.35047ZM9.17 15.0005C9.17 15.0005 12.91 13.4505 15.06 11.3005C20.46 5.90047 19.56 1.68047 19.27 0.730473C18.32 0.430473 14.1 -0.459527 8.7 4.94047C6.55 7.09047 5 10.8305 5 10.8305L9.17 15.0005ZM15.65 12.8105C13.36 14.8505 10.07 16.2505 9.76 16.3805L11.31 20.0005L15.36 15.9505C15.83 15.4805 16.04 14.8005 15.91 14.1405L15.65 12.8105ZM7 16.0005C7 16.8305 6.66 17.5805 6.12 18.1205C4.94 19.3005 0 20.0005 0 20.0005C0 20.0005 0.7 15.0605 1.88 13.8805C2.2988 13.4595 2.83325 13.1724 3.4155 13.0558C3.99775 12.9392 4.60153 12.9982 5.15017 13.2253C5.69882 13.4525 6.16758 13.8376 6.49694 14.3317C6.8263 14.8258 7.0014 15.4067 7 16.0005ZM11 7.00047C11 5.90047 11.9 5.00047 13 5.00047C14.1 5.00047 15 5.90047 15 7.00047C15 8.10047 14.1 9.00047 13 9.00047C11.9 9.00047 11 8.10047 11 7.00047Z" fill="#00696D"></path>
</svg>
0
</div>
<div>
<svg role="img" width="20" height="19" viewBox="0 0 20 19" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="likes-title">
<title id="likes-title">Likes</title>
<path d="M10 18.35L8.55 17.03C3.4 12.36 0 9.27 0 5.5C0 2.41 2.42 0 5.5 0C7.24 0 8.91 0.81 10 2.08C11.09 0.81 12.76 0 14.5 0C17.58 0 20 2.41 20 5.5C20 9.27 16.6 12.36 11.45 17.03L10 18.35Z"></path>
</svg>
0
</div>
</section>
<img class="cover-photo" src="https://images.ctfassets.net/mpqufjsy02zr/2FZlDcn2ESZMvM6IwXBekk/f7370ce6e91a15d2ed3c08d7751ff307/Twitter_post_-_2large-twitter-reducing-motion.png" alt="Text: Thoughts from reading Demystifying Disability by Emily Ladau with an illustration of a woman reading a book and sitting on top of three stacked books." />
<div class="blog-tags">
<a href="https://eevis.codes/tags/a11y"><span class="blog-tag turquoise">a11y</span></a> <a href="https://eevis.codes/tags/disability"><span class="blog-tag purple">disability</span></a>
</div>
<section class="player" aria-labelledby="player-description">
<p id="player-description">Listen to this blog post, read by Eevis:</p>
<div class="player-wrapper">
<audio id="player" controls="">
<source src="https://assets.ctfassets.net/mpqufjsy02zr/m9wwi2eTshz5QfRfxejLx/59dca4f59f5cf93d27071127630d1785/thoughts-from-demystifying-disability.mp3" type="audio/mpeg" />
<p>Your browser doesn't support HTML5 audio. Here is a <a href="https://assets.ctfassets.net/mpqufjsy02zr/m9wwi2eTshz5QfRfxejLx/59dca4f59f5cf93d27071127630d1785/thoughts-from-demystifying-disability.mp3">link to the recording</a> instead.</p>
</audio>
</div>
</section>
<nav aria-label="Thoughts From Reading Demystifying Disability by Emily Ladau Table of Contents" class="table-of-contents">
<h2>Table of Contents</h2>
<ol>
<li><a href="https://eevis.codes/blog/2021-11-06/thoughts-from-reading-demystifying-disability-by-emily-ladau/#person-first-language-vs-identity-first-language"> Person-First Language vs. Identity-First Language</a></li><li><a href="https://eevis.codes/blog/2021-11-06/thoughts-from-reading-demystifying-disability-by-emily-ladau/#ableism"> Ableism</a></li><li><a href="https://eevis.codes/blog/2021-11-06/thoughts-from-reading-demystifying-disability-by-emily-ladau/#inspiration-porn"> Inspiration Porn</a></li><li><a href="https://eevis.codes/blog/2021-11-06/thoughts-from-reading-demystifying-disability-by-emily-ladau/#wrapping-up"> Wrapping Up</a></li><li><a href="https://eevis.codes/blog/2021-11-06/thoughts-from-reading-demystifying-disability-by-emily-ladau/#links"> Links</a></li>
</ol>
</nav>
<p>I finished reading <a href="https://emilyladau.com/book/">Demystifying Disability by Emily Ladau</a> last week. It's a fantastic book discussing disability, ableism, accessibility, disability etiquette, and many more things. I highly recommend buying and reading or listening to it!</p>
<p>I have so many thoughts from reading the book, and I want to share some of them with you. And I know many more will come, as I often process new things over the course of a longer time. </p>
<h2 id="person-first-language-vs-identity-first-language">Person-First Language vs. Identity-First Language</h2>
<p>So, for those unfamiliar with the terms, person-first language (PFL) means using phrases where the person comes first. So, for example, <strong>people with disabilities</strong>. On the other hand, identity-first language (IFL) recognizes that disability is part of the person's identity, such as <strong>Autistic people</strong>. </p>
<p>Emily Ladau writes about her experiences with these approaches:</p>
<blockquote>
<p>Truth to be told, I went kind of overboard with my IFL evangelism at first. I could not understand why anyone would prefer PFL and pushed against it whenever I had the chance. </p>
</blockquote>
<p>I could say I was the opposite. At some point, it was difficult for me to understand why someone would like to go with the identity-first approach. Okay, I knew the reasons, but I could say I did not truly understand it. But now I do, and even better because of reading Demystifying Disability. </p>
<p>The book also got me thinking about why I've been feeling this way. For those of you who don't know, I had a concussion a few years back. I'm still recovering from it, and I know that things won't go back to what they were before that hit. </p>
<p>I was 26 when that happened, so I had a pretty strong sense of who I was before that. One thing was that I had an extraordinary memory. I could recall details so well that people around me found it strange sometimes.</p>
<p>And that was a big part of my identity. After the concussion, I realized I have problems with my memory, and I've had to come a long way to actually accept that this is my life now and I'm not the same person as I used to be back then. </p>
<p>But I'm guessing that this is one of the reasons I prefer to refer myself with the person-first language. I am still working with my identity because of the significant changes (memory is not the only one), and I assume I still will be for a long time. And that is okay.</p>
<p>As a final thought to this part, I want to remind you that it's a personal choice, and if someone prefers either one of these approaches, please do respect that. </p>
<h2 id="ableism">Ableism</h2>
<p>Okay, let's start again with some definitions. How Emily Ladau defines ableism:</p>
<blockquote>
<p>Ableism is attitudes, actions, and circumstances that devalue people because they are disabled or perceived as having a disability.</p>
</blockquote>
<p>Ableism is well-woven into our society, and we all echo these ableist biases and beliefs. And as with any type of systemic discrimination, it's hard to see if you're not part of the group that society discriminates. </p>
<p>I want to discuss two things related to ableism; systemic ableism and that people with disabilities can be ableist as well. </p>
<h3 id="systemic-ableism">Systemic Ableism</h3>
<p>Emily Ladau brings up the fact that ableism is a self-perpetuating cycle. I can fully agree with this and give an example from conversations I've had with people. </p>
<p>I often hear that the reason for not having accessible offices or using accessible apps and services in work environments is that "we don't have any people with disabilities working in our company." First, you wouldn't know, as people don't always want to disclose their disability statuses. </p>
<p>And then there's this: If your workplace is not accessible, how can a disabled person even come to an interview? If the applications or interviewing techniques you use for interviewing aren't accessible, how do you think you will be even able to hire someone who has barriers already before starting? </p>
<p>Again, Emily Ladau writes it well, saying that ableist assumptions lead to systemic ableism, which in turn leads to discrimination, despite being unintentional. </p>
<h3 id="people-with-disabilities-can-be-ableist-too">People with Disabilities Can Be Ableist Too</h3>
<p>I love how Emily Ladau points out that there is "no magical force field preventing disabled people being ableist." That is so true. Being a person with a disability doesn't mean I couldn't be ableist as well. </p>
<p>Even though I'm an accessibility specialist and disabled person myself, I still catch myself having these ableist thoughts. And I've definitely done some ableist things. But the thing is, we all do. From now on, we need to recognize these thoughts and actions. We need to try to be better and apologize if it's possible in that situation.</p>
<h2 id="inspiration-porn">Inspiration Porn</h2>
<p>Emily Ladau defines inspiration porn with the following words:</p>
<blockquote>
<p>[Inspiration porn] is an accurate way to describe the concept of how disabled people and their stories are objectified by the media to make observers feel warm and fuzzy or better about themselves.</p>
</blockquote>
<p>Late Stella Young popularized this term, and below, you can find her TEDx talk "I'm not your inspiration, thank you very much." Here's also <a href="http://www.humber.ca/makingaccessiblemedia/modules/01/transript/I'm_Not%20_Your_Inspirations_transcript.pdf">a link to the transcript of the talk</a>. </p>
<p><a href="https://www.youtube.com/watch?v=8K9Gg164Bsw">https://www.youtube.com/watch?v=8K9Gg164Bsw</a></p>
<p>I think that talk is so on the point of the problems of inspiration porn. Why are disabled people seen as inspirational just because they get up in the morning and get on with their days? Would you say someone without a disability is inspirational because of that? </p>
<p>I often pass as a nondisabled person because my disability is invisible. And truth to be told, one reason for hiding it's that I'm a bit afraid that I would be seen as inspirational for doing something that anyone else would do despite having a disability. </p>
<p>In her <a href="https://www.youtube.com/watch?v=g3EYc98Z0ug">axe-con-talk</a>, Haben Girma speaks about using the word "inspired" in the sense that what you are inspired to do when someone is inspiring. That's totally something I can get on board with. </p>
<p>So, when we feel like someone is inspiring us, let's think about why that is. Is it because seeing someone with a disability makes us feel better about ourselves, in a way that we believe that we are better than them? Or does it make us work towards an accessible world for all? </p>
<h2 id="wrapping-up">Wrapping Up</h2>
<p>I love Demystifying Disability and highly recommend it to everyone. It's an excellent overview of disability, ableism, accessibility, and many other themes. </p>
<p>In this blog post, I touched on only a couple of different themes in the book. As mentioned in the beginning, it gave me a lot to think about, and I believe there will be new realizations as I keep thinking about these themes. </p>
<h2 id="links">Links</h2>
<ul>
<li><a href="https://emilyladau.com/book/">Demystifying Disability by Emily Ladau</a></li>
<li><a href="https://www.youtube.com/watch?v=g3EYc98Z0ug">Haben Girma - Difference drives innovation & Disability Inclusion benefits all of us</a></li>
<li><a href="https://www.youtube.com/watch?v=8K9Gg164Bsw">Stella Young - I'm not your inspiration, thank you very much.</a></li>
<li><a href="http://www.humber.ca/makingaccessiblemedia/modules/01/transript/I'm_Not%20_Your_Inspirations_transcript.pdf">Transcript of Stella Young's talk</a></li>
</ul>
</article>
First Month as an Accessibility Specialist at Oura2021-11-22T00:00:00.000Zhttps://eevis.codes/blog/2021-11-22/first-month-as-an-accessibility-specialist-at-oura/
<nav id="breadcrumbs" aria-label="breadcrumbs">
<ol>
<li>
<a href="https://eevis.codes/blog/">Blog</a>
<span aria-hidden="true" class="breadcrumb-separator">/<span>
</span></span></li>
<li>First Month as an Accessibility Specialist at Oura</li><li>
</li></ol>
</nav>
<article class="blogPostLong">
<h1 id="/2021-11-22/first-month-as-an-accessibility-specialist-at-oura">First Month as an Accessibility Specialist at Oura</h1>
<div class="reading-time-and-date">
<p>
Reading time: about 3 minutes
</p>
<span class="separator"></span>
<p>Published <time datetime="22nd Nov, 2021">22nd Nov, 2021</time></p>
</div>
<section class="likes-and-boosts">
<div>
<svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="boosts-title">
<title id="boosts-title">Boosts</title>
<path d="M7.19 4.35047C5.15 6.64047 3.75 9.93047 3.62 10.2405L0 8.69047L4.05 4.64047C4.52 4.17047 5.2 3.96047 5.86 4.09047L7.19 4.35047ZM9.17 15.0005C9.17 15.0005 12.91 13.4505 15.06 11.3005C20.46 5.90047 19.56 1.68047 19.27 0.730473C18.32 0.430473 14.1 -0.459527 8.7 4.94047C6.55 7.09047 5 10.8305 5 10.8305L9.17 15.0005ZM15.65 12.8105C13.36 14.8505 10.07 16.2505 9.76 16.3805L11.31 20.0005L15.36 15.9505C15.83 15.4805 16.04 14.8005 15.91 14.1405L15.65 12.8105ZM7 16.0005C7 16.8305 6.66 17.5805 6.12 18.1205C4.94 19.3005 0 20.0005 0 20.0005C0 20.0005 0.7 15.0605 1.88 13.8805C2.2988 13.4595 2.83325 13.1724 3.4155 13.0558C3.99775 12.9392 4.60153 12.9982 5.15017 13.2253C5.69882 13.4525 6.16758 13.8376 6.49694 14.3317C6.8263 14.8258 7.0014 15.4067 7 16.0005ZM11 7.00047C11 5.90047 11.9 5.00047 13 5.00047C14.1 5.00047 15 5.90047 15 7.00047C15 8.10047 14.1 9.00047 13 9.00047C11.9 9.00047 11 8.10047 11 7.00047Z" fill="#00696D"></path>
</svg>
0
</div>
<div>
<svg role="img" width="20" height="19" viewBox="0 0 20 19" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="likes-title">
<title id="likes-title">Likes</title>
<path d="M10 18.35L8.55 17.03C3.4 12.36 0 9.27 0 5.5C0 2.41 2.42 0 5.5 0C7.24 0 8.91 0.81 10 2.08C11.09 0.81 12.76 0 14.5 0C17.58 0 20 2.41 20 5.5C20 9.27 16.6 12.36 11.45 17.03L10 18.35Z"></path>
</svg>
0
</div>
</section>
<img class="cover-photo" src="https://images.ctfassets.net/mpqufjsy02zr/4LmVbvywgg6sN1RZF0j0ME/532a4ad43e64011364f9980a2b9a2107/first-months-as-an-accessibility-specialist.png" alt="Text: First months as an accessibility specialist at Oura with an illustration of a long, dark haired woman working with a laptop and tablet." />
<div class="blog-tags">
<a href="https://eevis.codes/tags/general"><span class="blog-tag turquoise">general</span></a> <a href="https://eevis.codes/tags/a11y"><span class="blog-tag turquoise">a11y</span></a> <a href="https://eevis.codes/tags/work"><span class="blog-tag orange">work</span></a>
</div>
<section class="player" aria-labelledby="player-description">
<p id="player-description">Listen to this blog post, read by Eevis:</p>
<div class="player-wrapper">
<audio id="player" controls="">
<source src="https://assets.ctfassets.net/mpqufjsy02zr/6YqNiNxuitOld0OaRGZhJJ/c177abfbf6f1452abfc64a2cfbe21116/First_months_as_accessibility_specialist.mp3" type="audio/mpeg" />
<p>Your browser doesn't support HTML5 audio. Here is a <a href="https://assets.ctfassets.net/mpqufjsy02zr/6YqNiNxuitOld0OaRGZhJJ/c177abfbf6f1452abfc64a2cfbe21116/First_months_as_accessibility_specialist.mp3">link to the recording</a> instead.</p>
</audio>
</div>
</section>
<nav aria-label="First Month as an Accessibility Specialist at Oura Table of Contents" class="table-of-contents">
<h2>Table of Contents</h2>
<ol>
<li><a href="https://eevis.codes/blog/2021-11-22/first-month-as-an-accessibility-specialist-at-oura/#i-dont-have-to-sell-myself-or-accessibility"> I Don't Have to Sell Myself or Accessibility</a></li><li><a href="https://eevis.codes/blog/2021-11-22/first-month-as-an-accessibility-specialist-at-oura/#im-expected-to-concentrate-on-accessibility"> I'm Expected to Concentrate on Accessibility</a></li><li><a href="https://eevis.codes/blog/2021-11-22/first-month-as-an-accessibility-specialist-at-oura/#im-not-alone"> I'm not alone</a></li><li><a href="https://eevis.codes/blog/2021-11-22/first-month-as-an-accessibility-specialist-at-oura/#wrapping-up"> Wrapping Up</a></li><li><a href="https://eevis.codes/blog/2021-11-22/first-month-as-an-accessibility-specialist-at-oura/#links"> Links</a></li>
</ol>
</nav>
<p>So, I joined Oura Health in October, and it's been a little bit over a month now. My role is an accessibility specialist, and I'm the first person to be hired in the company in such a role. </p>
<p>During the past month, we've had busy times. We launched the new Gen3 ring, and it has brought a bustle. On the other hand, it's been super cool to join the company during such a time. </p>
<p>From an accessibility perspective, I know that we aren't there yet. We have a long road to go to change things, but one thing I'm excited about is that there is a will to do that.</p>
<p>In this blog post, I'll share some initial thoughts and feelings from the past month. I'm super thrilled about what is coming, and I'm feeling happy that I made this change. </p>
<h2 id="i-dont-have-to-sell-myself-or-accessibility">I Don't Have to Sell Myself or Accessibility</h2>
<p>One of the best things has been realizing that I don't need to sell myself to do accessibility-related work anymore. It has been given due to my role. It might sound obvious, but it is not for me.</p>
<p>You see, I've been in a position before where I've expressed that I want to do more accessibility-related work. I've been very vocal about that. In the end, I've still been the front-end dev first and foremost, who doesn't have time to look into making the results of my work accessible. If accessibility didn't take too much time, it was ok to think about, but there were never actual resources for that. </p>
<p>Those situations happened when I was working in a consultancy. I was sold as a front-end developer, and that was what the customer was paying for. They didn't have accessibility in their roadmaps, so they didn't want to spend money on that. And myself being a consultant, well, I cost money. </p>
<p>So I've been super happy that whenever I'm talking about changing things due to accessibility reasons, just saying that is enough. I don't have to justify to people how they benefit from it. </p>
<p>That means a lot. It has allowed me to use my skills and energy to actually do something without having to spend a long time selling the idea. </p>
<h2 id="im-expected-to-concentrate-on-accessibility">I'm Expected to Concentrate on Accessibility</h2>
<p>Continuing from the previous point, I'm super happy that I can concentrate on accessibility. It's actually expected of me. I kind of knew that from day one, but after a couple of weeks, I had this moment when it really hit me. I'm not the front-end developer anymore, but the one who does accessibility. </p>
<p>That also means that I don't need to agree to complete something first, and then, if there is still time, I can look into accessibility-specific matters. No, I get to do it right away. That's expected of me. </p>
<p>One example is that right now, I'm doing an audit of one of our services. I even got my own epic to do that. And it feels good. Maybe you might think that hey, that's nothing special. Well, for me, it is. Coming from a situation where I didn't get to do what I wanted, this feels amazing.</p>
<h2 id="im-not-alone">I'm not alone</h2>
<p>Another thing that's great at Oura is that I'm not alone in my accessibility mission. There are people across the company who understand the value of accessible services and that we need to do better. Yes, as mentioned, there's still a lot to do, but I feel that I'm not alone in this. </p>
<p>Heck, we even have this part in our values:</p>
<blockquote>
<p>Humanity is present in all that we do—connecting with kindness and empathy. We build products and culture that celebrate and support uniqueness, inclusivity, and diversity, where everyone belongs. We meet people where they are, as they are, and <strong>prioritize accessibility.</strong></p>
</blockquote>
<p>I've had so many great conversations with people during the past weeks. And what has surprised me the most was that people were waiting for me to start. In multiple different conversations, I've heard that "Oh, I've been waiting for you to join! I have so many questions for you!" That feels just amazing. </p>
<h2 id="wrapping-up">Wrapping Up</h2>
<p>So, as you might have guessed, I'm excited. There's certainly so much to do, but I get to work with talented people who care about their users. I believe that in the future, we can really say that our data is accessible, if not for all, then at least for a wider range of people. </p>
<h2 id="links">Links</h2>
<ul>
<li><a href="https://ouraring.com/about-us">Oura - About Us</a></li>
</ul>
</article>
Aria-Label is Not Always the Answer2021-11-29T00:00:00.000Zhttps://eevis.codes/blog/2021-11-29/aria-label-is-not-always-the-answer/
<nav id="breadcrumbs" aria-label="breadcrumbs">
<ol>
<li>
<a href="https://eevis.codes/blog/">Blog</a>
<span aria-hidden="true" class="breadcrumb-separator">/<span>
</span></span></li>
<li>Aria-Label is Not Always the Answer</li><li>
</li></ol>
</nav>
<article class="blogPostLong">
<h1 id="/2021-11-29/aria-label-is-not-always-the-answer">Aria-Label is Not Always the Answer</h1>
<div class="reading-time-and-date">
<p>
Reading time: about 4 minutes
</p>
<span class="separator"></span>
<p>Published <time datetime="29th Nov, 2021">29th Nov, 2021</time></p>
</div>
<section class="likes-and-boosts">
<div>
<svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="boosts-title">
<title id="boosts-title">Boosts</title>
<path d="M7.19 4.35047C5.15 6.64047 3.75 9.93047 3.62 10.2405L0 8.69047L4.05 4.64047C4.52 4.17047 5.2 3.96047 5.86 4.09047L7.19 4.35047ZM9.17 15.0005C9.17 15.0005 12.91 13.4505 15.06 11.3005C20.46 5.90047 19.56 1.68047 19.27 0.730473C18.32 0.430473 14.1 -0.459527 8.7 4.94047C6.55 7.09047 5 10.8305 5 10.8305L9.17 15.0005ZM15.65 12.8105C13.36 14.8505 10.07 16.2505 9.76 16.3805L11.31 20.0005L15.36 15.9505C15.83 15.4805 16.04 14.8005 15.91 14.1405L15.65 12.8105ZM7 16.0005C7 16.8305 6.66 17.5805 6.12 18.1205C4.94 19.3005 0 20.0005 0 20.0005C0 20.0005 0.7 15.0605 1.88 13.8805C2.2988 13.4595 2.83325 13.1724 3.4155 13.0558C3.99775 12.9392 4.60153 12.9982 5.15017 13.2253C5.69882 13.4525 6.16758 13.8376 6.49694 14.3317C6.8263 14.8258 7.0014 15.4067 7 16.0005ZM11 7.00047C11 5.90047 11.9 5.00047 13 5.00047C14.1 5.00047 15 5.90047 15 7.00047C15 8.10047 14.1 9.00047 13 9.00047C11.9 9.00047 11 8.10047 11 7.00047Z" fill="#00696D"></path>
</svg>
0
</div>
<div>
<svg role="img" width="20" height="19" viewBox="0 0 20 19" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="likes-title">
<title id="likes-title">Likes</title>
<path d="M10 18.35L8.55 17.03C3.4 12.36 0 9.27 0 5.5C0 2.41 2.42 0 5.5 0C7.24 0 8.91 0.81 10 2.08C11.09 0.81 12.76 0 14.5 0C17.58 0 20 2.41 20 5.5C20 9.27 16.6 12.36 11.45 17.03L10 18.35Z"></path>
</svg>
0
</div>
</section>
<img class="cover-photo" src="https://images.ctfassets.net/mpqufjsy02zr/1ZEK0v1IYY0biGSzSxEpX/001a7769952bf05d26617a587ae6a487/aria-label-is-not-always-the-answer.png" alt="Aria-Label is not Always the Answer with an illustration of woman standing and looking at a screen with rectangles representing text, and lines going from the texts to outside the screen to another lines annotating aria-labels." />
<div class="blog-tags">
<a href="https://eevis.codes/tags/webdev"><span class="blog-tag blue">webdev</span></a> <a href="https://eevis.codes/tags/a11y"><span class="blog-tag turquoise">a11y</span></a>
</div>
<section class="player" aria-labelledby="player-description">
<p id="player-description">Listen to this blog post, read by Eevis:</p>
<div class="player-wrapper">
<audio id="player" controls="">
<source src="https://assets.ctfassets.net/mpqufjsy02zr/10h8PS8ISXu0VAg4ECJLsN/e77c38f75dc75fe6346b30163e720363/Aria-label-is-not-always-the-answer.mp3" type="audio/mpeg" />
<p>Your browser doesn't support HTML5 audio. Here is a <a href="https://assets.ctfassets.net/mpqufjsy02zr/10h8PS8ISXu0VAg4ECJLsN/e77c38f75dc75fe6346b30163e720363/Aria-label-is-not-always-the-answer.mp3">link to the recording</a> instead.</p>
</audio>
</div>
</section>
<nav aria-label="Aria-Label is Not Always the Answer Table of Contents" class="table-of-contents">
<h2>Table of Contents</h2>
<ol>
<li><a href="https://eevis.codes/blog/2021-11-29/aria-label-is-not-always-the-answer/#what-is-aria-label"> What Is <code>aria-label</code>?</a></li><li><a href="https://eevis.codes/blog/2021-11-29/aria-label-is-not-always-the-answer/#how-does-aria-label-work"> How does <code>aria-label</code> work?</a></li><li><a href="https://eevis.codes/blog/2021-11-29/aria-label-is-not-always-the-answer/#aria-label-and-non-supported-elements"> <code>aria-label</code> and non-supported elements</a></li><li><a href="https://eevis.codes/blog/2021-11-29/aria-label-is-not-always-the-answer/#options-for-aria-label"> Options for <code>aria-label</code></a></li><li><a href="https://eevis.codes/blog/2021-11-29/aria-label-is-not-always-the-answer/#wrapping-up"> Wrapping Up</a></li><li><a href="https://eevis.codes/blog/2021-11-29/aria-label-is-not-always-the-answer/#links"> Links</a></li>
</ol>
</nav>
<p>Okay, you've learned about accessibility and that there is sometimes a need to make, for example, the link text more descriptive for screen reader users. You've learned about the <code>aria-label</code>-attribute, and its powers. It's the answer to everything!</p>
<p>I'm afraid I have to say that it is not. It doesn't work with all HTML elements. In this blog post, I'll dig a bit deeper into the <code>aria-label</code>-attribute, and its usage and limitations, and options. </p>
<h2 id="what-is-aria-label">What Is <code>aria-label</code>?</h2>
<p>So, let's first talk about the specification and where to use it. <a href="https://www.w3.org/TR/wai-aria/">W3C Recommendation for Accessible Rich Internet Applications (WAI-ARIA)-document</a> defines <code>aria-label</code> as "a string value that labels the current element." You can use it to provide a recognizable name of the object if there is no visible text on the screen that could be used as the label. If there was, <code>aria-labelledby</code> should be used. </p>
<p>The most important thing, and the reason I'm writing this article, is that developers should use it only with the following elements:</p>
<ul>
<li>interactive elements (such as links, buttons, inputs et cetera)</li>
<li>Elements that have a landmark role</li>
<li>Elements that have an ARIA-widget role</li>
<li><code>img</code> or <code>iframe</code>-elements</li>
</ul>
<p>Source: <a href="https://www.tpgi.com/short-note-on-aria-label-aria-labelledby-and-aria-describedby/">Short note about <code>aria-label,</code> <code>aria-labelledby</code> and <code>aria-describedby</code> by Léonie Watson.</a></p>
<p><code>aria-label</code> on these elements works consistently with assistive technologies. The support is inconsistent if you use it with any other tag, such as <code>div</code> or <code>span.</code> More about that in <a href="https://eevis.codes/blog/2021-11-29/aria-label-is-not-always-the-answer/#aria-label-and-non-supported-elements">Aria-label and non-supported elements</a>.</p>
<p>Let's look into each one of these elements a bit more.</p>
<h3 id="interactive-elements">Interactive Elements</h3>
<p>Interactive elements are tags intended for user interaction. It means, for example, <code>button</code>, <code>a</code> (when <code>href</code>-attribute is present), <code>input</code> , <code>details</code> and others. The main idea is that users can interact with them. </p>
<h3 id="elements-with-landmark-role">Elements with Landmark Role</h3>
<p>Elements can have landmark roles either implicitly or explicitly. Having an implicit role means that some of the HTML elements have roles set to them natively. The explicit role, on the other hand, is set with <code>role</code>-attribute. </p>
<p>I'll list the different roles and elements that already have those roles:</p>
<table>
<caption>Landmark-roles with HTML-elements having those roles</caption>
<thead>
<tr>
<th>Role</th>
<th>HTML-element</th>
</tr>
</thead>
<tbody>
<tr>
<td>banner</td>
<td>(document) <code><header></code></td>
</tr>
<tr>
<td>complementary</td>
<td><code><aside></code></td>
</tr>
<tr>
<td>contentinfo</td>
<td>(document) <code><footer></code></td>
</tr>
<tr>
<td>form</td>
<td><form> (if provided an accessible name via <code>aria-label</code> or <code>aria-labelledby</code>)</td>
</tr><tr>
<td>main</td>
<td><code><main></code></td>
</tr><tr><td>navigation</td>
<td><code><nav></code></td>
</tr><tr><td>region</td>
<td><code><section></code></td>
</tr>
</tbody>
</table>
<p>Source: <a href="https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles#3._landmark_roles">MDN's list of landmark roles</a></p>
<p>In addition to the roles listed, there's a <code>search</code>-role, which is only an explicit role, so, set with <code>role</code>-attribute.</p>
<h3 id="elements-with-aria-widget-role">Elements with ARIA-widget Role</h3>
<p>ARIA-widget roles define typical interactive patterns. These patterns usually need JavaScript to implement their intended behavior. There are multiple ARIA-widget roles, and I'm going to list a couple of them:</p>
<ul>
<li><code>tab</code></li>
<li><code>tablist</code></li>
<li><code>searchbox</code></li>
<li><code>treeitem</code></li>
<li><code>switch</code></li>
</ul>
<p>You can find more from <a href="https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles#2._widget_roles">MDN's list of ARIA-widget roles</a>.</p>
<h3 id="img-and-iframe"><code>img</code> and <code>iframe</code></h3>
<p>Image and iframe are two elements where <code>aria-label</code> works as well. For <code>img,</code> there are rare cases when you might need <code>aria-label.</code> In general, you should use the <code>alt</code>-attribute to provide an accessible name for the image. </p>
<h2 id="how-does-aria-label-work">How does <code>aria-label</code> work?</h2>
<p><code>aria-label</code> overrides the visible text in elements, where the accessible name would come from the child element. For example:</p>
<pre><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span> <span class="token attr-name">aria-label</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Something else<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
click me
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span>
</code></pre><p>The above button would be exposed as "Something else" to screen reader users.</p>
<h2 id="aria-label-and-non-supported-elements"><code>aria-label</code> and non-supported elements</h2>
<p>So, as mentioned, the support for <code>aria-label</code> is inconsistent with, for example, <code>div</code> and <code>span.</code> If the <code>div</code> or <code>span</code> doesn't have any role, <code>aria-label</code> is ignored. The same goes for other non-interactive elements, such as <code>p,</code> <code>ul,</code> <code>li,</code> or <code>legend</code> as well. </p>
<p> If they have a landmark role, support is still inconsistent. Per <a href="https://www.w3.org/TR/using-aria/#notes2">Notes on ARIA Use in HTML</a>, some widget roles work better than others:</p>
<blockquote>
<p>Its fine to use aria-label or aria-labelledby on div elements with role=navigation, role=search, role=main, JAWS doesn't support them on role=banner, role=complementary, role=contentinfo. NVDA, VoiceOver, and Talkback are OK.</p>
</blockquote>
<h2 id="options-for-aria-label">Options for <code>aria-label</code></h2>
<p>If <code>aria-label</code> is not the answer, then what is? Well, it depends. In general, I would recommend adding visible text. Usually, when you're adding text for screen-reader users, it would be helpful for sighted users as well. </p>
<p>However, if a non-visible text is needed, you could add visually hidden text with CSS. You can read more and find a CSS-snippet from The A11Y Project's article <a href="https://www.a11yproject.com/posts/2013-01-11-how-to-hide-content/">Hide Content</a>.</p>
<h2 id="wrapping-up">Wrapping Up</h2>
<p><code>aria-label</code> is a helpful tool in cases when you need to add an accessible name for an element, and there is no already visible text to be used. However, you can't use it with every HTML tag. In general, those tags that don't work are non-interactive elements. </p>
<p>There are exceptions: If, for example, <code>div</code> has one of the roles listed above, then screen readers read it. But there are inconsistencies with some roles: for example, roles <code>banner</code>, <code>complementary,</code> and <code>contentinfo</code> are something that JAWS ignores, while VoiceOver, TalkBack, and NVDA support them. </p>
<p>I listed two options for cases when <code>aria-label</code> doesn't work. You could add a visible text, as it usually would be helpful for all users. Another option is to add visually hidden text. </p>
<h2 id="links">Links</h2>
<ul>
<li><a href="https://www.w3.org/TR/wai-aria/">W3C Recommendation for Accessible Rich Internet Applications (WAI-ARIA)-document</a></li>
<li><a href="https://www.tpgi.com/short-note-on-aria-label-aria-labelledby-and-aria-describedby/">Short note about <code>aria-label,</code> <code>aria-labelledby</code> and <code>aria-describedby</code> by Léonie Watson.</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles#3._landmark_roles">MDN's list of landmark roles</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles#2._widget_roles">MDN's list of ARIA-widget roles</a></li>
<li><a href="https://www.w3.org/TR/using-aria/#notes2">Notes on ARIA Use in HTML</a></li>
<li>The A11y Project - <a href="https://www.a11yproject.com/posts/2013-01-11-how-to-hide-content/">Hide Content</a></li>
</ul>
</article>
How Not to Create a Button2021-12-13T00:00:00.000Zhttps://eevis.codes/blog/2021-12-13/how-not-to-create-a-button/
<nav id="breadcrumbs" aria-label="breadcrumbs">
<ol>
<li>
<a href="https://eevis.codes/blog/">Blog</a>
<span aria-hidden="true" class="breadcrumb-separator">/<span>
</span></span></li>
<li>How Not to Create a Button</li><li>
</li></ol>
</nav>
<article class="blogPostLong">
<h1 id="/2021-12-13/how-not-to-create-a-button">How Not to Create a Button</h1>
<div class="reading-time-and-date">
<p>
Reading time: about 6 minutes
</p>
<span class="separator"></span>
<p>Published <time datetime="13th Dec, 2021">13th Dec, 2021</time></p>
</div>
<section class="likes-and-boosts">
<div>
<svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="boosts-title">
<title id="boosts-title">Boosts</title>
<path d="M7.19 4.35047C5.15 6.64047 3.75 9.93047 3.62 10.2405L0 8.69047L4.05 4.64047C4.52 4.17047 5.2 3.96047 5.86 4.09047L7.19 4.35047ZM9.17 15.0005C9.17 15.0005 12.91 13.4505 15.06 11.3005C20.46 5.90047 19.56 1.68047 19.27 0.730473C18.32 0.430473 14.1 -0.459527 8.7 4.94047C6.55 7.09047 5 10.8305 5 10.8305L9.17 15.0005ZM15.65 12.8105C13.36 14.8505 10.07 16.2505 9.76 16.3805L11.31 20.0005L15.36 15.9505C15.83 15.4805 16.04 14.8005 15.91 14.1405L15.65 12.8105ZM7 16.0005C7 16.8305 6.66 17.5805 6.12 18.1205C4.94 19.3005 0 20.0005 0 20.0005C0 20.0005 0.7 15.0605 1.88 13.8805C2.2988 13.4595 2.83325 13.1724 3.4155 13.0558C3.99775 12.9392 4.60153 12.9982 5.15017 13.2253C5.69882 13.4525 6.16758 13.8376 6.49694 14.3317C6.8263 14.8258 7.0014 15.4067 7 16.0005ZM11 7.00047C11 5.90047 11.9 5.00047 13 5.00047C14.1 5.00047 15 5.90047 15 7.00047C15 8.10047 14.1 9.00047 13 9.00047C11.9 9.00047 11 8.10047 11 7.00047Z" fill="#00696D"></path>
</svg>
0
</div>
<div>
<svg role="img" width="20" height="19" viewBox="0 0 20 19" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="likes-title">
<title id="likes-title">Likes</title>
<path d="M10 18.35L8.55 17.03C3.4 12.36 0 9.27 0 5.5C0 2.41 2.42 0 5.5 0C7.24 0 8.91 0.81 10 2.08C11.09 0.81 12.76 0 14.5 0C17.58 0 20 2.41 20 5.5C20 9.27 16.6 12.36 11.45 17.03L10 18.35Z"></path>
</svg>
0
</div>
</section>
<img class="cover-photo" src="https://images.ctfassets.net/mpqufjsy02zr/1HIrnvly2xzuvf9051NE4A/77d55416ce30e12b32c19f4226d306cb/Twitter_post_-_5how-not-to-create-a-button__1_.png" alt="How not to create a button. Illustration has a woman standing and looking at UI with couple of buttons." />
<div class="blog-tags">
<a href="https://eevis.codes/tags/a11y"><span class="blog-tag turquoise">a11y</span></a> <a href="https://eevis.codes/tags/html"><span class="blog-tag purple">html</span></a>
</div>
<nav aria-label="How Not to Create a Button Table of Contents" class="table-of-contents">
<h2>Table of Contents</h2>
<ol>
<li><a href="https://eevis.codes/blog/2021-12-13/how-not-to-create-a-button/#div-as-a-button"> Div as a Button</a></li><li><a href="https://eevis.codes/blog/2021-12-13/how-not-to-create-a-button/#link-as-a-button"> Link as a Button</a></li><li><a href="https://eevis.codes/blog/2021-12-13/how-not-to-create-a-button/#link-with-a-role-of-button"> Link with a Role of Button</a></li><li><a href="https://eevis.codes/blog/2021-12-13/how-not-to-create-a-button/#button-element-wrapping-a-link"> Button Element Wrapping a Link</a></li><li><a href="https://eevis.codes/blog/2021-12-13/how-not-to-create-a-button/#checkbox-label-as-a-button"> Checkbox Label as a Button</a></li><li><a href="https://eevis.codes/blog/2021-12-13/how-not-to-create-a-button/#the-right-way-to-create-a-button"> The Right Way to Create a Button</a></li><li><a href="https://eevis.codes/blog/2021-12-13/how-not-to-create-a-button/#links"> Links</a></li>
</ol>
</nav>
<p>I have a habit of testing websites with a keyboard. I often encounter buttons that don't work as expected. It's frustrating, and I usually start digging into the source code to understand why the button isn't working. These reasons vary - some of them could be described as "normalized" patterns (I disagree), and some are really imaginative. </p>
<p>I'll share some ways to <strong>not</strong> create a button in this blog post. I'll also explain why those patterns aren't a good idea. You can find instructions on creating a button the right way in the last section, <a href="https://eevis.codes/blog/2021-12-13/how-not-to-create-a-button/#the-right-way-to-create-a-button">The Right Way to Create a Button</a>. </p>
<h2 id="div-as-a-button">Div as a Button</h2>
<p>This <code>div</code> as a button pattern is by far the most common. Here's a code snippet to demonstrate it:</p>
<pre><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token special-attr"><span class="token attr-name">onClick</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value javascript language-javascript"><span class="token spread operator">...</span></span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span>
I pretend to be a button
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span>
</code></pre><p>Sometimes these elements even have <code>tabindex="0"</code> to make them focusable with a keyboard. But they never, ever seem to have the keyboard event listeners for <kbd>space</kbd> and <kbd>enter</kbd>. This means that even if the user can focus on the so-called button, they can't activate it with a keyboard.</p>
<p>Another thing that is often missing is the role attribute to communicate that there is a button. When it's missing, non-sighted screen reader users won't even know any button exists. For them, that particular element is a focusable text they might encounter if they're browsing the page's content.</p>
<p>So, while this pattern works for mouse users, it's excluding most of the other user groups out there. It's possible to make a <code>div</code> work as a button for all these groups, but it's lots of work and makes the code harder to maintain. And you know, by using the button element, you'd get all that for free.</p>
<h2 id="link-as-a-button">Link as a Button</h2>
<p>Another HTML element I often see used as a button is the anchor-element, so, link. In these cases, it's styled to look like a button. However, underneath it all, it's a link:</p>
<pre><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>...<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
I, too, pretend to be a button
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span>
</code></pre><p>"But call to actions are a thing, right?" you might ask. Yeah, I know, they're a pattern that is widely used. And it's a bit confusing pattern - they often look like a button but then lead to another part of the website. And they don't initiate an action, such as opening a modal.</p>
<p>From the usage point of view, it doesn't make much difference for a sighted mouse user, whether it's a link or button under the hood. I mean, it works when clicking, be it a button or a link. </p>
<p>However, for non-mouse users, it does. For example, keyboard users have certain ways to interact with an element. They can activate a button with <kbd>space</kbd> and <kbd>enter</kbd>, and link only with <kbd>enter</kbd>. This means, that the so-called button is not actionable with <kbd>space</kbd>.</p>
<p>And why it is a problem? Well, have you ever pressed <kbd>space</kbd> on a site when it's not focused anywhere? It scrolls the page down. And that's what happens when you're focused on a link and press <kbd>space</kbd>. It's frustrating as user needs to scroll back up where they were. It might be even painful for some users in cases where every extra keystroke causes pain. </p>
<p>And then there's this: When users don't know that there is a link underneath the button, they don't have the control to use it as they would with a link. This means that they might, for example, want to open the link to a new tab or window. </p>
<h2 id="link-with-a-role-of-button">Link with a Role of Button</h2>
<pre><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>button<span class="token punctuation">"</span></span> <span class="token attr-name">tabindex</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0<span class="token punctuation">"</span></span> <span class="token special-attr"><span class="token attr-name">onClick</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token value javascript language-javascript"><span class="token punctuation">{</span>…<span class="token punctuation">}</span></span></span></span><span class="token punctuation">></span></span>
I'm a link, I'm a button. Who am I?
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span>
</code></pre><p>This one is my new favorite. I mean, it's a creative way of making a button even more confusing for many. For screen reader users, it indeed seems like a button. It even works if they try to activate it with screen reader commands, as they trigger the click-handler. However, this type of button doesn't work with keyboard navigation.</p>
<p>Why, you ask? Isn't it a link, so it works with <kbd>enter</kbd>, right? The answer is no. That element is not a link because it no longer has the <code>href</code>-attribute. Removing the <code>href</code>-attribute strips away the semantics and keyboard interaction. This way, even the enter-key doesn't work with the element. </p>
<p>I think the most common reason for using this solution is libraries and frameworks hiding away the semantics of an element. My guess for this particular case is that there is some kind of CSS-in-JS-library in use, and the developer has needed the link's styles for a button. Then they have extended the link component and added all the attributes.</p>
<h2 id="button-element-wrapping-a-link">Button Element Wrapping a Link</h2>
<p>Okay, the previous two examples are relatively common ones. The following two are (hopefully) not that popular. </p>
<p>First, I present to you a button element wrapping a link:</p>
<pre><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>...<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
I'm a link wrapped in a button. And I don't really work
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span>
</code></pre><p>First, let's start with the fact that this is incorrect HTML. A link can't be inside a button, as the HTML specification defines the button's content model:</p>
<figure>
<blockquote>
<p>Phrasing content, but there must be no interactive content descendant and no descendant with the tabindex attribute specified.</p>
</blockquote>
<figcaption><cite><a href="https://html.spec.whatwg.org/multipage/form-elements.html#the-button-element">The button-element</a></cite> in HTML-specification</figcaption>
</figure>
<p>Even though anchor-tag is phrasing content, it's also interactive, so this rules it out. </p>
<p>This pattern also does not work well for screen reader or keyboard users. Because the link is wrapped with a button, there's an extra tab stop before the user reaches the actual link. And as the button doesn't have a click-handler, trying to activate it doesn't do anything. So, the user feels like they're on a control, but it doesn't work, as the button still gets focused.</p>
<p>That is really frustrating and confusing for the user. If they try to go forward and tab to the link element and then press <kbd>enter</kbd>, they'll get the "button" to work. However, it is not clear that there is a next tab stop. And with the link, all the same problems presented in the previous section remain. </p>
<p>For screen reader users, it presents even more problems. I tested this pattern with VoiceOver and couldn't get it to work at all. I'm not sure about the other screen readers, but I'd guess they also have problems.</p>
<h2 id="checkbox-label-as-a-button">Checkbox Label as a Button</h2>
<p>This pattern has been the wildest of them all. And at the same time, I understand how a developer has ended up with this pattern. So, in this case, we have a checkbox-input, which is visually hidden, and then the label is styled to look like a button. The code:</p>
<pre><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>label</span> <span class="token attr-name">htmlFor</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>checkbox<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
I also want to be a button
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>input</span>
<span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>checkbox<span class="token punctuation">"</span></span>
<span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>checkbox<span class="token punctuation">"</span></span>
<span class="token attr-name">checked</span> <span class="token punctuation">/></span></span>
</code></pre><p>As mentioned, I understand how the developer ended up with this. There's this idea of changing something based on two states. The data that this particular checkbox represents can either be on or off. And based on that state, there are changes in the UI. </p>
<p>However, the checkbox serves another role in the UI. As Heydon Pickering mentions in <a href="http://book.inclusive-components.design/">Inclusive Components</a>, users might suspect that they're also choosing a value for submission. This, however, is true only for screen reader users, as they hear that the underlying component is a checkbox. </p>
<p>The problem for sighted users is that as the label is styled to look like a button, they expect it's a button. But if they try to interact with it using a keyboard, it gets confusing. You see, the key that toggles a checkbox is <kbd>space</kbd>. So it doesn't work with <kbd>enter</kbd> at all. </p>
<p>So, after all these not-so-great ways of creating a button, let's look into making it the right way.</p>
<h2 id="the-right-way-to-create-a-button">The Right Way to Create a Button</h2>
<p>It's pretty simple. Use the <code><button></code>-element. It has everything out of the box: the role of a button to convey its semantics, tab index to put it into tab order, and easiness of giving it a click handler that handles the keyboard interaction as well. </p>
<pre><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span> <span class="token special-attr"><span class="token attr-name">onClick</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token value javascript language-javascript"><span class="token punctuation">{</span><span class="token spread operator">...</span><span class="token punctuation">}</span></span></span></span><span class="token punctuation">></span></span>
I am actual button
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span>
</code></pre><p>This helps your and your colleagues' lives, as the code is easier to maintain. Also, using semantic elements helps the users - not everyone uses a mouse, so having the expected keyboard interaction is required. You don't want to exclude anyone, right?</p>
<h2 id="links">Links</h2>
<ul>
<li><a href="https://html.spec.whatwg.org/multipage/form-elements.html#the-button-element">The button-element</a></li>
<li><a href="http://book.inclusive-components.design/">Inclusive Components</a> by Heydon Pickering</li>
</ul>
</article>
Year in Review - 2021 Edition2022-01-01T00:00:00.000Zhttps://eevis.codes/blog/2022-01-01/year-in-review-2021-edition/
<nav id="breadcrumbs" aria-label="breadcrumbs">
<ol>
<li>
<a href="https://eevis.codes/blog/">Blog</a>
<span aria-hidden="true" class="breadcrumb-separator">/<span>
</span></span></li>
<li>Year in Review - 2021 Edition</li><li>
</li></ol>
</nav>
<article class="blogPostLong">
<h1 id="/2022-01-01/year-in-review-2021-edition">Year in Review - 2021 Edition</h1>
<div class="reading-time-and-date">
<p>
Reading time: about 9 minutes
</p>
<span class="separator"></span>
<p>Published <time datetime="1st Jan, 2022">1st Jan, 2022</time></p>
</div>
<section class="likes-and-boosts">
<div>
<svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="boosts-title">
<title id="boosts-title">Boosts</title>
<path d="M7.19 4.35047C5.15 6.64047 3.75 9.93047 3.62 10.2405L0 8.69047L4.05 4.64047C4.52 4.17047 5.2 3.96047 5.86 4.09047L7.19 4.35047ZM9.17 15.0005C9.17 15.0005 12.91 13.4505 15.06 11.3005C20.46 5.90047 19.56 1.68047 19.27 0.730473C18.32 0.430473 14.1 -0.459527 8.7 4.94047C6.55 7.09047 5 10.8305 5 10.8305L9.17 15.0005ZM15.65 12.8105C13.36 14.8505 10.07 16.2505 9.76 16.3805L11.31 20.0005L15.36 15.9505C15.83 15.4805 16.04 14.8005 15.91 14.1405L15.65 12.8105ZM7 16.0005C7 16.8305 6.66 17.5805 6.12 18.1205C4.94 19.3005 0 20.0005 0 20.0005C0 20.0005 0.7 15.0605 1.88 13.8805C2.2988 13.4595 2.83325 13.1724 3.4155 13.0558C3.99775 12.9392 4.60153 12.9982 5.15017 13.2253C5.69882 13.4525 6.16758 13.8376 6.49694 14.3317C6.8263 14.8258 7.0014 15.4067 7 16.0005ZM11 7.00047C11 5.90047 11.9 5.00047 13 5.00047C14.1 5.00047 15 5.90047 15 7.00047C15 8.10047 14.1 9.00047 13 9.00047C11.9 9.00047 11 8.10047 11 7.00047Z" fill="#00696D"></path>
</svg>
0
</div>
<div>
<svg role="img" width="20" height="19" viewBox="0 0 20 19" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="likes-title">
<title id="likes-title">Likes</title>
<path d="M10 18.35L8.55 17.03C3.4 12.36 0 9.27 0 5.5C0 2.41 2.42 0 5.5 0C7.24 0 8.91 0.81 10 2.08C11.09 0.81 12.76 0 14.5 0C17.58 0 20 2.41 20 5.5C20 9.27 16.6 12.36 11.45 17.03L10 18.35Z"></path>
</svg>
0
</div>
</section>
<img class="cover-photo" src="https://images.ctfassets.net/mpqufjsy02zr/1ieXXmfajNXZeX1ACIb8Zu/a7cda345190c264a98b2b28747166d5f/Twitter_post_-_6year-in-review__1_.png" alt="Year in Review - 2021 Edition. Illustration has a woman talking to a phone and looking at her computer. It also has the year 2021 written in the top right corner." />
<div class="blog-tags">
<a href="https://eevis.codes/tags/yearly-review"><span class="blog-tag turquoise">yearly-review</span></a>
</div>
<nav aria-label="Year in Review - 2021 Edition Table of Contents" class="table-of-contents">
<h2>Table of Contents</h2>
<ol>
<li><a href="https://eevis.codes/blog/2022-01-01/year-in-review-2021-edition/#blogging"> Blogging</a></li><li><a href="https://eevis.codes/blog/2022-01-01/year-in-review-2021-edition/#speaking"> Speaking</a></li><li><a href="https://eevis.codes/blog/2022-01-01/year-in-review-2021-edition/#accomplishments"> Accomplishments </a></li><li><a href="https://eevis.codes/blog/2022-01-01/year-in-review-2021-edition/#things-you-probably-didnt-see"> Things You Probably Didn't See</a></li><li><a href="https://eevis.codes/blog/2022-01-01/year-in-review-2021-edition/#so-whats-next"> So, What's Next?</a></li>
</ol>
</nav>
<p>It's that time of the year again: time to look back and summarize what has happened last year. And when I started listing different things, oh boy, what a year has it been! </p>
<p>This blog post will look into some of the highs and lows of 2021 in my life. I also checked what I've added to Polywork and made a collection of 2021 highlights. There are some additional things there, so if you want to have a look, here's the link: </p>
<p><a href="https://www.polywork.com/eevis/collections/868636">2021 Highlights in Polywork</a></p>
<p>Let's dive in.</p>
<h2 id="blogging">Blogging</h2>
<p>So, I started blogging a little over a year ago. First, I was publishing my posts in Dev.to. In the fall, I finally put together my own blog, and since then, I've published the posts first there and then cross-posted to Dev.to. </p>
<p>In addition to that, I've published a couple of other blog posts, one in Mimmit Koodaa (a Finnish organization working towards equality in tech) and in my previous workplace's blog. </p>
<p>I want to mention a couple of my favorites:</p>
<article class="blog-embed">
<h3>Don't Develop Just for Yourself - A Developer's Checklist to Accessibility</h3>
<p>Published at <time datetime="2021-05-28">May 28</time></p>
<a href="https://eevis.codes/blog/2021-05-28/dont-develop-just-for-yourself-a-developers-checklist-to-accessibility">Read the post Don't Develop Just for Yourself - A Developer's Checklist to Accessibility</a>
</article>
<p>This blog post has been part of my talk with the same name, and I've shared it when speaking at conferences. I've also been able to use it at work; I've been constructing checklists for PR templates, and this blog post has been really helpful. </p>
<article class="blog-embed">
<h3>Why I'm Not One of the Guys</h3>
<p>Published at <time datetime="2021-07-24">Jul 24</time></p>
<a href="https://eevis.codes/blog/2021-07-24/why-im-not-one-of-the-guys">Read the post Why I'm Not One of the Guys</a>
</article>
<p>This blog post has been one of the hardest to write and publish. When I cross-posted it to Dev, I got tons of comments - many telling me how wrong I am to feel excluded. Some of them were really rude, and I noticed I'm still reluctant to publish posts in Dev after that. </p>
<p>In addition to rude comments, I got supportive comments, and I am super happy about them. So thank you all who wrote them!</p>
<p>I guess one of the reasons I feel so bad after the comments is that this was the first time I faced this much hatred towards my opinions on the internet. And I'm sad to see that in the developer community, there are so many people who want to ignore the feelings of those in minorities.</p>
<p>Although this was the hardest to write and publish, I'm proud that I did it. </p>
<article class="blog-embed">
<h3>5 Things I've Learned as a Female Developer</h3>
<p>Published at <time datetime="2021-01-24">Jan 24</time></p>
<a href="https://dev.to/eevajonnapanula/5-things-i-ve-learned-as-a-female-developer-p19">Read the post 5 Things I've Learned as a Female Developer</a>
</article>
<p>The points I list in this blog post are something I've come back to throughout the year. I've been either reminding myself about them or the others in different instances. The most important point has been that nobody knows everything. And yet, we often feel bad and talk ourselves down because we think we should know everything. </p>
<p>Oh, and one more accomplishment in blogging: I've started recording my blog posts. That means they will be available in both written and audio formats and thus serve more people. </p>
<h2 id="speaking">Speaking</h2>
<p>Last year I was also speaking a lot compared to the previous year. There was a total of 11 different events, plus some internal talks. Of these 11, seven were conferences, one was a vodcast, and the rest were meetups or other speaking engagements. </p>
<p>The talk for the year was definitely "Don't Develop Just for Yourself - A Developer's Checklist to Accessibility." I gave it in six events. I also talked about the theme of "Reduced motion" and equality in tech. </p>
<p>I'll list three of my favorite events and say a couple of words about them. </p>
<h3 id="euruko-2021">Euruko 2021</h3>
<p>Euruko was so much fun! It was organized remotely, and they streamed some things from an actual studio. Most of the speakers gave their talks remotely, but I got to go to the studio to speak. </p>
<p>Even though we had some minor technical difficulties, the experience was incredible. I mean, I got to meet some real people Also the production was good.</p>
<p>However, the community was the key to making the event fantastic. Even though I don't use Ruby or Rails, I was already talking with someone that I was already telling people that I will mark Euruko 2022 in my calendar right away because of the community. So, see you then!</p>
<h3 id="mimmit-koodaa-this-is-not-a-webinar">Mimmit Koodaa This is Not a Webinar</h3>
<p>Okay, this is something I just need to mention. It was the event where I received the Mimmit Koodaa-award (more on that later). But that was not all - I also gave a talk at the event with my (former) colleague. </p>
<p>We spoke about the importance of role models and representation and how to encourage representatives of minorities to be those role models. </p>
<p> It was my first talk where I collaborated with somebody. As the Covid-situation did not let us practice in person (and the talk was streamed live from a studio, so we were at the same place), I was super nervous. But it went well! </p>
<p>And it actually led to an opportunity to be a guest in a radio program for the Finnish Broadcasting Company (YLE). So, I also made my first visit to the radio in 2021.</p>
<h3 id="inclusive-design-24">Inclusive Design 24</h3>
<p>Speaking at Inclusive Design 24 was definitely one of the year's highlights. Even though I've decided my strategy in speaking about accessibility is to get to the conferences that are not about accessibility, ID24 was something I have dreamed of for some time. And when the email came that they accepted my talk, I was thrilled. </p>
<p>I spoke about reduced motion, a theme dear to me. The response was great, and I also got to meet one of my role models in the accessibility world, Sarah Higley, as she was hosting the session.</p>
<p>Also, the other talks were so good. I've been watching the recordings and got some ideas from them. For example, the <a href="https://www.youtube.com/watch?v=aGl38gB37zA&list=PLn7dsvRdQEfFoUIFxtSsp8PjHm-glki1Z">talk about Auditorial</a> gave me the idea to start recording my blog posts and adding a possibility to listen to them. </p>
<h2 id="accomplishments">Accomplishments</h2>
<p>When looking back last year, it's been a year of accomplishments. I'll name a few.</p>
<h3 id="mimmit-koodaa-award">Mimmit Koodaa-award</h3>
<p>First, <strong>Mimmit Koodaa-award</strong>. I was the first person in history to receive the award for my volunteering and my work for equality and breaking the IT industry's stereotypes. It's a great honor. One fantastic thing regarding the award was that my role model, former president of Finland Tarja Halonen, presented it. She has done so much for equality and has had a remarkable career. I highly recommend reading about her, if you haven't yet. </p>
<h3 id="finalist-in-nordic-women-in-tech-awards">Finalist in Nordic Women in Tech Awards</h3>
<p>Continuing with the awards theme, I was nominated and shortlisted to be a finalist in the Nordic Women in Tech Awards in the category Rising star of the year. That was an honor!</p>
<h3 id="naisten-linja">Naisten Linja</h3>
<p><a href="https://naistenlinja.fi/">Naisten Linja</a> works to help women and girls who have faced violence. I've been working to help build their website as a volunteer. That has been one of the most meaningful things for the last year. The work they do is super important.</p>
<h3 id="started-at-oura">Started at Oura</h3>
<p>And finally, the one thing I'm super happy about is that I switched jobs from the consulting world to a product company to work as an accessibility specialist. If you want to read my initial thoughts about the first month, head to this blog post I wrote: <a href="https://eevis.codes/blog/2021-11-22/first-month-as-an-accessibility-specialist-at-oura/">First Month as an Accessibility Specialist at Oura</a>.</p>
<h2 id="things-you-probably-didnt-see">Things You Probably Didn't See</h2>
<h3 id="i-got-tired">I Got Tired</h3>
<p>It was a challenging year. Not from the remote working perspective, no. That is actually something I enjoy. But there have been some other contributors that have led me to be on sickness leave. </p>
<p>It has been primarily work-related, which was one reason I switched jobs. Right now, I'm happy to be working in a role that I have control over, and I get to concentrate on accessibility.</p>
<p>Last spring was hard, and there were so many changes and uncertainties in the project I was working on. And this was even though the project team was fantastic! Many things worked smoothly, and we had good communication with the client. We had control over many things related to the project we were doing. People on the team - both from our and the client's side - were nice. </p>
<p>However, I was tired when the summer was approaching because of uncertainties and other things. To be honest, I was exhausted. And I realized that even though we had control over the project-related things, the uncertainties came from the corporation level, and no one in our team could do anything about it.</p>
<p>Maybe the best decision for the whole spring was to call the occupational health doctor and get sickness leave for the last two weeks before the holidays. </p>
<p>When the worst was over and I started to see more clearly, I realized that I needed change. Many of the problems that caused my condition were because I was working in a consultancy. So I sent an application to the one company I had often said that I would apply to when I wanted to switch away from consulting. I could say that the rest is history. Or some other cliché. </p>
<h3 id="exploring-the-nature">Exploring the Nature</h3>
<p>I am one of those who picked up a new outdoor hobby in 2020. I learned kayaking in a local club, and I could say that was one of the best decisions ever. I've seen so many incredible, beautiful places that you can only reach by water.. And I've continued learning this year, and my next goal is to pass Euro Padel Pass level 2. </p>
<p>Another thing I've picked up in the last two years has been hiking. I've visited some Finnish national parks and can only repeat that Finnish nature is stunning. And nights in a hammock, away from all the rush and negativity on the internet, that is just amazing.</p>
<p>To give some numbers from this theme, I visited 11 out of 41 Finnish national parks last year. I plan to visit all of them at some point, but as I live in the south and many are in Lapland, it will take some time to accomplish.</p>
<h2 id="so-whats-next">So, What's Next?</h2>
<p>I don't want to set anything in stone. These past years have shown that anything can change in an instant, and so can my plans. However, there are some themes I'd love to see being part of my life this year. </p>
<h3 id="cognitive-accessibility">Cognitive Accessibility</h3>
<p>This year, I want to learn more about cognitive accessibility. I think I have a basic knowledge about it, but I want to go deeper. </p>
<p>This goal is related to so many things - work, studies (technical communications), and personal life. I want to understand it better and to be able to help people around me to create content that is more accessible for everyone, and especially for those who need cognitive accessibility.</p>
<h3 id="accessibility-content-in-finnish">Accessibility Content in Finnish</h3>
<p>I've noticed that writing (and speaking) about accessibility is more natural for me in English. My approach to content creation around accessibility has been about the content I needed - and one thing I want to improve is that I create content in Finnish as well. That's something I totally would have needed when I started learning about coding and accessibility.</p>
<p>So, I have some ideas for content creation in Finnish in 2022. However, I will not stress myself out about it.</p>
<h3 id="spend-more-time-outdoors">Spend More Time Outdoors</h3>
<p>As mentioned in the previous section, I've picked up some covid-safe hobbies such as kayaking and hiking. I want to continue with those and spend some more time outdoors in 2022. I also want to spend more nights outside in my hammock, preferably after a day of kayaking in different places. And when there is no water around, hiking is okay too.</p>
<p>So, to conclude this post, I want to wish you a happy New Year, may it be better than the previous ones!</p>
</article>
How I Created Neule.art2022-06-30T00:00:00.000Zhttps://eevis.codes/blog/2022-06-30/how-i-created-neule-art/
<nav id="breadcrumbs" aria-label="breadcrumbs">
<ol>
<li>
<a href="https://eevis.codes/blog/">Blog</a>
<span aria-hidden="true" class="breadcrumb-separator">/<span>
</span></span></li>
<li>How I Created Neule.art</li><li>
</li></ol>
</nav>
<article class="blogPostLong">
<h1 id="/2022-06-30/how-i-created-neule-art">How I Created Neule.art</h1>
<div class="reading-time-and-date">
<p>
Reading time: about 8 minutes
</p>
<span class="separator"></span>
<p>Published <time datetime="30th Jun, 2022">30th Jun, 2022</time></p>
</div>
<section class="likes-and-boosts">
<div>
<svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="boosts-title">
<title id="boosts-title">Boosts</title>
<path d="M7.19 4.35047C5.15 6.64047 3.75 9.93047 3.62 10.2405L0 8.69047L4.05 4.64047C4.52 4.17047 5.2 3.96047 5.86 4.09047L7.19 4.35047ZM9.17 15.0005C9.17 15.0005 12.91 13.4505 15.06 11.3005C20.46 5.90047 19.56 1.68047 19.27 0.730473C18.32 0.430473 14.1 -0.459527 8.7 4.94047C6.55 7.09047 5 10.8305 5 10.8305L9.17 15.0005ZM15.65 12.8105C13.36 14.8505 10.07 16.2505 9.76 16.3805L11.31 20.0005L15.36 15.9505C15.83 15.4805 16.04 14.8005 15.91 14.1405L15.65 12.8105ZM7 16.0005C7 16.8305 6.66 17.5805 6.12 18.1205C4.94 19.3005 0 20.0005 0 20.0005C0 20.0005 0.7 15.0605 1.88 13.8805C2.2988 13.4595 2.83325 13.1724 3.4155 13.0558C3.99775 12.9392 4.60153 12.9982 5.15017 13.2253C5.69882 13.4525 6.16758 13.8376 6.49694 14.3317C6.8263 14.8258 7.0014 15.4067 7 16.0005ZM11 7.00047C11 5.90047 11.9 5.00047 13 5.00047C14.1 5.00047 15 5.90047 15 7.00047C15 8.10047 14.1 9.00047 13 9.00047C11.9 9.00047 11 8.10047 11 7.00047Z" fill="#00696D"></path>
</svg>
0
</div>
<div>
<svg role="img" width="20" height="19" viewBox="0 0 20 19" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="likes-title">
<title id="likes-title">Likes</title>
<path d="M10 18.35L8.55 17.03C3.4 12.36 0 9.27 0 5.5C0 2.41 2.42 0 5.5 0C7.24 0 8.91 0.81 10 2.08C11.09 0.81 12.76 0 14.5 0C17.58 0 20 2.41 20 5.5C20 9.27 16.6 12.36 11.45 17.03L10 18.35Z"></path>
</svg>
0
</div>
</section>
<img class="cover-photo" src="https://images.ctfassets.net/mpqufjsy02zr/2pF0Gd0FMopIJXzIaFK8wS/5798380f415b6e57e3750b155931010d/Twitter_post_-_8solving-non-coding-problems__2_.png" alt="How I Created Neule.art" />
<div class="blog-tags">
<a href="https://eevis.codes/tags/webdev"><span class="blog-tag blue">webdev</span></a> <a href="https://eevis.codes/tags/html"><span class="blog-tag purple">html</span></a> <a href="https://eevis.codes/tags/knitting"><span class="blog-tag orange">knitting</span></a>
</div>
<nav aria-label="How I Created Neule.art Table of Contents" class="table-of-contents">
<h2>Table of Contents</h2>
<ol>
<li><a href="https://eevis.codes/blog/2022-06-30/how-i-created-neule-art/#the-planning-and-decisions"> The Planning and Decisions</a></li><li><a href="https://eevis.codes/blog/2022-06-30/how-i-created-neule-art/#getting-the-svg"> Getting the SVG</a></li><li><a href="https://eevis.codes/blog/2022-06-30/how-i-created-neule-art/#building-with-eleventy"> Building with Eleventy</a></li><li><a href="https://eevis.codes/blog/2022-06-30/how-i-created-neule-art/#publishing"> Publishing</a></li><li><a href="https://eevis.codes/blog/2022-06-30/how-i-created-neule-art/#sharing-the-site"> Sharing the Site</a></li><li><a href="https://eevis.codes/blog/2022-06-30/how-i-created-neule-art/#what-ive-added-after-the-launch"> What I've Added After the Launch</a></li><li><a href="https://eevis.codes/blog/2022-06-30/how-i-created-neule-art/#the-future"> The Future</a></li><li><a href="https://eevis.codes/blog/2022-06-30/how-i-created-neule-art/#links-in-the-blog-post"> Links in the Blog Post</a></li>
</ol>
</nav>
<p>I'm a knitter. It's one way of expressing my creativity and also passing the time. And it's sometimes super relaxing. And I (and people around me) get to wear some nice, warm, self-made garments. </p>
<p>One ongoing project is that I'm knitting some <a href="https://lopidesign.is/en/shop/adults/sweaters/riddari/">Riddari-sweaters</a> as presents to some of my close ones when they turn 30. Usually, I've just decided on the colors myself, but this time I wanted to ask from the recipient for their color choice. </p>
<p>So, I could have tried to find some pictures of the sweater in potential colors. But there was a chance that I couldn't find those. And it takes some imagination to try to think how some colors play together if you only see a picture of the yarn ball and the sweater in random color. Trust me; I've bought some new yarn in the middle of making a sweater because what I was knitting was not what I had imagined.</p>
<p>I needed a way of visualizing the colors somehow. And thus, the idea for my next project was born. In this blog post, I'll introduce you to <a href="https://neule.art/">Neule.art</a>, a color picker or color visualizer for the Riddari sweater (among other patterns), and how I created it. </p>
<p><img src="https://images.ctfassets.net/sk24pdnnlwhc/FfcY9iC5yFwAJHV7sW24c/5c73b11e47496af782457462834738bf/168206375-61d14250-f4dd-46c2-8475-37ae3ab51e1e.png" alt="A part of website, with a text "Pick your colors for Riddari" as the heading, and a drawn picture of an Icelandic sweater (Riddari-pattern) with colors black, lagoon, apricot and heaven blue. Next to the shirt there is a form with selects for four colors, and colors mentioned are selected." /></p>
<p>If you're interested in the code, you can find it from the <a href="https://github.com/eevajonnapanula/neule.art/">Neule.art repository</a>. The code is nowhere perfect, and I know I could polish it a lot, but my goal has been just to get it out and then, maybe at some point, improve the code quality.</p>
<h2 id="the-planning-and-decisions">The Planning and Decisions</h2>
<p>So, I began pondering how to create this site or app. I instantly started thinking about using SVGs for the visualization, as you can manipulate the colors of an SVG pretty straightforwardly. </p>
<p>Another thing to decide on was the technologies. What should I use? React? NextJS? Something else? I wanted to build a page with the least possible amount of JavaScript, so I decided to go with Eleventy. That's a framework I've used before, and my website, for example, is built on Eleventy.</p>
<p>I'm a bit bored with JavaScript and wanted to try if I could build the site without any client-side JS. As Eleventy is a static site generator, this is possible. Even though I use JS for development, the result can be without JavaScript - if I want it that way. But how can the site manage changing colors? Eleventy Serverless and HTML forms to the rescue.</p>
<p>Let's talk next about how I implemented the different components mentioned above.</p>
<h2 id="getting-the-svg">Getting the SVG</h2>
<p>SVG was a great idea, but there was a problem: there were no SVGs for the Riddari-sweater I knew of. First, I thought about drawing the sweater. It sounded like a great plan - until I remembered that I'm not much of an artist. So no drawing.</p>
<p>After spending some time on the internet, I came across image tracing. That sounded like a plan, and after finally purchasing Procreate on my iPad and playing around a bit, I started tracing the shirt. It looked awesome.</p>
<p>It was just that there was no SVG export from the Procreate app. What do? Well, the thing I do best: search for answers. After some time, I had installed Inkscape, and after trial and error, I finally had the shirt in SVG, where it was possible to manipulate colors by CSS.</p>
<h2 id="building-with-eleventy">Building with Eleventy</h2>
<p>Building the site could have been a tricky part. Fortunately, I had experience with Eleventy, specifically with serverless functions and Eleventy. </p>
<p>I also wanted to use HTML to its total capacity - in this case, it means using forms and form actions. I also didn't want to use JS on the site unless it was <em>absolutely</em> necessary. And spoiler alert: I didn't have any client-side JS in the first version of the site. Okay, I use Eleventy (which is, indeed, a JavaScript library) for building the site, but everything works with HTML in the production site. </p>
<p>At the time of writing, I'm building some progressive enhancements to change the colors dynamically without reloading the page. However, I will still keep in mind those who don't want to or can't have JavaScript enabled in the browser.</p>
<p>But back to the process. I started building the site. </p>
<p>First, I created a static site, showing the SVG of the shirt with default values. Then I added the Eleventy Serverless plugin and dynamic path to the site displaying the SVG. This way, I could pass the four colors for the shirt as a query parameter. </p>
<h3 id="using-the-native-html-form">Using the Native HTML Form</h3>
<p>Once I had done that, adding the form was the next step. If you're unfamiliar with HTML native forms, they work so that when you hit "send" (or whatever the primary action is), they send the values from the form as an object to the URL you've defined in the <code>action</code>-attribute. And if you use the "get"-method, you get the values as query parameters - which is perfect for what I was doing.</p>
<p> So, here's a code snippet I'm using in the project (I edited it and stripped away all Nunjucks-syntax I use for passing the data for clarity):</p>
<pre><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>form</span> <span class="token attr-name">method</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>get<span class="token punctuation">"</span></span> <span class="token attr-name">action</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>/en/colors/<span class="token punctuation">"</span></span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>colors-form<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>label</span> <span class="token attr-name">for</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>color-a<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>A (Main color):<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>select</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>color-a<span class="token punctuation">"</span></span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>a<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>option</span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0059<span class="token punctuation">"</span></span> <span class="token attr-name">selected</span><span class="token punctuation">></span></span>Black (0059)<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>option</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>option</span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>9423<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Lagoon (9423)<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>option</span><span class="token punctuation">></span></span>
<span class="token comment"><!-- More color options --></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>select</span><span class="token punctuation">></span></span>
<span class="token comment"><!-- More color selects --></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>submit<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Check the result<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>form</span><span class="token punctuation">></span></span>
</code></pre><h3 id="getting-the-data-for-yarn-availability">Getting the Data For Yarn Availability</h3>
<p>Another thing I wanted to add to the site was a possibility to see if some selected stores had specific colorways available of the Lèttlopi-yarn (which knitters often use for Icelandic sweaters). And by "selected," I mean stores I either knew have Léttlopi on stock or were easily found on Google. </p>
<p>As I had this approach of not using client-side JavaScript, I needed to store the data in some way Eleventy could utilize it. Also, I didn't want to scrape the sites every time users visited my page. After some consideration, I wrote a function that runs once a day at night and scrapes the available colors from the selected yarn stores' sites. </p>
<p>I use Github actions and cron job for running the function. It scrapes through the yarn pages in the stores, parses the data to JSON, and then saves the new values into a data file. Then the site gets rebuilt, using the updated data for yarn availability.</p>
<p>Writing the scraper was fun. I used <a href="https://cheerio.js.org/">cheerio</a> to get and find the relevant data from the document on the yarn stores' websites. Then I parsed it with JavaScript to JSON. The fun in this was that every site has its way of annotating available yarn. Every store was a new puzzle to solve so that I get the relevant piece of information - which colors of the yarn were available and which were not. </p>
<p>If you go and check the <a href="https://github.com/eevajonnapanula/neule.art/blob/main/helpers/getColors.js">getColors.js</a>-file, you can see that I've been using different techniques for different stores. Sometimes it used an id, sometimes Regex, sometimes splitting the string from specific places, and sometimes looking for a class name. </p>
<p>When I had the yarn data available, I just needed to use it. And then, I had all the pieces together and had the MVP (minimum viable product) ready for publishing. </p>
<h2 id="publishing">Publishing</h2>
<p>As I had decided to use serverless functions, and as Eleventy has instructions only for Netlify, it was pretty straightforward to use Netlify to host the site. Also, Netlify itself is relatively straightforward, so setting it up was fast - and the fact that I've used Netlify for many things in the past helped in that.</p>
<p>But the most challenging part of publishing the site was to buy the domain - or, rather, decide the domain name for the site. After pondering (and reading the list of possible top-level domains) for some time, it hit me. "Neule.art!" It's perfect. "Neule" means knit garment in Finnish, and hey, knitting is art. Also, from the beginning, I had plans to add other patterns than Riddari to the site, so I didn't want to use Riddari for the domain name. </p>
<p>I bought the domain, spent some time figuring out all the DNS stuff, and finally, the site was live! I was so happy. You know, it's not always obvious that one gets their side project published. I've started so many projects I've never finished, so it feels good to complete something finally. And that it is something I feel proud of and can share. </p>
<h2 id="sharing-the-site">Sharing the Site</h2>
<p>I shared the site with some of my friends, and their response was encouraging. So, I decided to share the project on LinkedIn. Suddenly, it got so many comments and likes, and someone shared it on Facebook's Icelandic sweater/yarn-related groups. On the first day, I got a couple of thousands of visits (it's a lot for me and a niche page).</p>
<p>What was super encouraging were the comments and feedback people shared with me. I was solving a problem for myself - and solved it for many others at the same time. It feels great to be able to help.</p>
<h2 id="what-ive-added-after-the-launch">What I've Added After the Launch</h2>
<p>I launched the site in mid-May. I've been working to improve it since then. I've added a possibility to generate random colors, added a version of the Riddari-sweater where users can change color for every motif (instead of the original pattern's four colors), and added a new yarn store (Lanka-Kaisa). </p>
<p>I've also done a lot of under-the-hood fixes and features, such as adding cypress tests, fixing bugs, and improving the SEO of the website. </p>
<h2 id="the-future">The Future</h2>
<p>I have plans to add more patterns and yarns to the site. Also, as mentioned, I'm working on improving the user experience by providing the possibility to change colors dynamically. </p>
<p>Also, I'm happy to hear ideas and feature requests to the site - you can either contact me (<a href="https://eevis.codes/blog/2022-06-30/how-i-created-neule-art/hello@eevis.codes">hello@eevis.codes</a>) or send <a href="https://github.com/eevajonnapanula/neule.art/issues/new?assignees=&labels=&template=feature_request.md&title=">a feature request through Github</a>. </p>
<h2 id="links-in-the-blog-post">Links in the Blog Post</h2>
<ul>
<li><a href="https://lopidesign.is/en/shop/adults/sweaters/riddari/">Riddari-sweaters</a></li>
<li><a href="https://neule.art/">Neule.art</a></li>
<li><a href="https://github.com/eevajonnapanula/neule.art/">Neule.art repository</a></li>
<li><a href="https://cheerio.js.org/">cheerio</a> </li>
<li><a href="https://github.com/eevajonnapanula/neule.art/blob/main/helpers/getColors.js">getColors.js</a></li>
<li><a href="https://eevis.codes/blog/2022-06-30/how-i-created-neule-art/hello@eevis.codes">hello@eevis.codes</a></li>
<li>Send <a href="https://github.com/eevajonnapanula/neule.art/issues/new?assignees=&labels=&template=feature_request.md&title=">a feature request through Github</a></li>
</ul>
</article>
How Can Backend Developers Improve Accessibility?2022-07-07T00:00:00.000Zhttps://eevis.codes/blog/2022-07-07/how-can-backend-developers-improve-accessibility/
<nav id="breadcrumbs" aria-label="breadcrumbs">
<ol>
<li>
<a href="https://eevis.codes/blog/">Blog</a>
<span aria-hidden="true" class="breadcrumb-separator">/<span>
</span></span></li>
<li>How Can Backend Developers Improve Accessibility?</li><li>
</li></ol>
</nav>
<article class="blogPostLong">
<h1 id="/2022-07-07/how-can-backend-developers-improve-accessibility">How Can Backend Developers Improve Accessibility?</h1>
<div class="reading-time-and-date">
<p>
Reading time: about 6 minutes
</p>
<span class="separator"></span>
<p>Published <time datetime="7th Jul, 2022">7th Jul, 2022</time></p>
</div>
<section class="likes-and-boosts">
<div>
<svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="boosts-title">
<title id="boosts-title">Boosts</title>
<path d="M7.19 4.35047C5.15 6.64047 3.75 9.93047 3.62 10.2405L0 8.69047L4.05 4.64047C4.52 4.17047 5.2 3.96047 5.86 4.09047L7.19 4.35047ZM9.17 15.0005C9.17 15.0005 12.91 13.4505 15.06 11.3005C20.46 5.90047 19.56 1.68047 19.27 0.730473C18.32 0.430473 14.1 -0.459527 8.7 4.94047C6.55 7.09047 5 10.8305 5 10.8305L9.17 15.0005ZM15.65 12.8105C13.36 14.8505 10.07 16.2505 9.76 16.3805L11.31 20.0005L15.36 15.9505C15.83 15.4805 16.04 14.8005 15.91 14.1405L15.65 12.8105ZM7 16.0005C7 16.8305 6.66 17.5805 6.12 18.1205C4.94 19.3005 0 20.0005 0 20.0005C0 20.0005 0.7 15.0605 1.88 13.8805C2.2988 13.4595 2.83325 13.1724 3.4155 13.0558C3.99775 12.9392 4.60153 12.9982 5.15017 13.2253C5.69882 13.4525 6.16758 13.8376 6.49694 14.3317C6.8263 14.8258 7.0014 15.4067 7 16.0005ZM11 7.00047C11 5.90047 11.9 5.00047 13 5.00047C14.1 5.00047 15 5.90047 15 7.00047C15 8.10047 14.1 9.00047 13 9.00047C11.9 9.00047 11 8.10047 11 7.00047Z" fill="#00696D"></path>
</svg>
0
</div>
<div>
<svg role="img" width="20" height="19" viewBox="0 0 20 19" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="likes-title">
<title id="likes-title">Likes</title>
<path d="M10 18.35L8.55 17.03C3.4 12.36 0 9.27 0 5.5C0 2.41 2.42 0 5.5 0C7.24 0 8.91 0.81 10 2.08C11.09 0.81 12.76 0 14.5 0C17.58 0 20 2.41 20 5.5C20 9.27 16.6 12.36 11.45 17.03L10 18.35Z"></path>
</svg>
0
</div>
</section>
<img class="cover-photo" src="https://images.ctfassets.net/mpqufjsy02zr/2ghMzKTGsUr7D5qf3TX3Ny/16db80018cca3f15906503a3dd923856/Twitter_post_-_9solving-non-coding-problems.png" alt="How can Backend Developers Improve Accessibility?" />
<div class="blog-tags">
<a href="https://eevis.codes/tags/webdev"><span class="blog-tag blue">webdev</span></a> <a href="https://eevis.codes/tags/a11y"><span class="blog-tag turquoise">a11y</span></a> <a href="https://eevis.codes/tags/html"><span class="blog-tag purple">html</span></a> <a href="https://eevis.codes/tags/backend"><span class="blog-tag turquoise">backend</span></a>
</div>
<nav aria-label="How Can Backend Developers Improve Accessibility? Table of Contents" class="table-of-contents">
<h2>Table of Contents</h2>
<ol>
<li><a href="https://eevis.codes/blog/2022-07-07/how-can-backend-developers-improve-accessibility/#api-structure"> API structure</a></li><li><a href="https://eevis.codes/blog/2022-07-07/how-can-backend-developers-improve-accessibility/#documentation"> Documentation</a></li><li><a href="https://eevis.codes/blog/2022-07-07/how-can-backend-developers-improve-accessibility/#performance"> Performance</a></li><li><a href="https://eevis.codes/blog/2022-07-07/how-can-backend-developers-improve-accessibility/#backend-rendered-pages"> Backend-rendered pages</a></li><li><a href="https://eevis.codes/blog/2022-07-07/how-can-backend-developers-improve-accessibility/#wrapping-up"> Wrapping Up</a></li><li><a href="https://eevis.codes/blog/2022-07-07/how-can-backend-developers-improve-accessibility/#links"> Links</a></li>
</ol>
</nav>
<p>This question might feel a bit strange. Backend developers often touch the UI less, so they don't need to care, right? I beg to differ. This blog post will look at some practices backend developers can do to ensure the accessibility of the end product and documentation.</p>
<p>We'll look into API structure, documentation, performance aspects, and backend rendered pages. This is in no way an exhaustive list of things, and there is definitely more backend devs can do, but let's start with these four.</p>
<h2 id="api-structure">API structure</h2>
<p>There is actually a huge opportunity to improve accessibility when building APIs. This opportunity comes from reusable structures and ensuring that the content requires accessible roles, names, states, or properties. Also, APIs can enforce requiring things like having captions and transcripts for videos and audio content. </p>
<p>One concrete example of API supporting accessibility is images. When uploading an image, the API should require an alternative text or information that the image is decorative. So, when validating the sent input, there should be a check for having the alt-text (for example, as a separate parameter). And if the image is purely decorative, and thus the alt-text is not needed, then, for example, a boolean value indicating that. </p>
<p>Localization is another thing that helps with accessibility. Enabling people to use products in their native language can make barriers smaller. When designing an API, developers should consider localization and how that API can support it. I know there are cases where another service handles localization, or it's handled in the front end, but it's rarely only about the texts on the site. Often values stored in the database should be localizable too. </p>
<h2 id="documentation">Documentation</h2>
<p>Another aspect where backend developers can help with accessibility is the documentation. There are two sides to this: The site or platform, and the content.</p>
<h3 id="platform-or-site">Platform or Site</h3>
<p>First of all, the site where the documentation is should be accessible. So, for example, the site uses semantic HTML, is keyboard navigable, has colors that have enough color contrast, and has a logical and clear structure. You can read more about <a href="https://www.w3.org/WAI/fundamentals/accessibility-intro/">web accessibility from Web Accessibility Initiative</a>.</p>
<p>Many projects use readymade solutions to render documentation. Whether it's a Swagger-type documentation containing only endpoints and inputs, or a more extensive platform with text, it's not usually built from scratch. So, it's essential to check that the platform actually is accessible. </p>
<p>You can <a href="https://dev.to/eevajonnapanula/automated-accessibility-testing-is-a-good-start-but-you-need-to-test-manually-too-13f2">use automated testing tools</a> and also <a href="https://eevis.codes/blog/2021-05-28/dont-develop-just-for-yourself-a-developers-checklist-to-accessibility/">do some manual checks</a>. These blogposts contain resources for development, but there are tools and assessments you can do for any site.</p>
<p>I'm in the process of doing some quick accessibility tests for different documentation tools, and I will write a blog post about the results. So stay tuned!</p>
<h3 id="content">Content</h3>
<p>Another important thing is that the actual language in the documentation is accessible. You can use some of the principles of <a href="https://www.plainlanguage.gov/about/definitions/">plain language</a>. Let's look at what this means in practice.</p>
<p>If the documentation has longer text blocks, try to follow these instructions:</p>
<ul>
<li>When you address the reader, use "you," not passive.</li>
<li>Avoid complex sentences and unnecessary words.</li>
<li>Write the most important information first.</li>
<li>Write short sentences and paragraphs.</li>
</ul>
<p>Also, if the documentation has images, remember to add alternative texts explaining the image's message. If the picture is purely decorative, add <code>alt=""</code> to it. Here's <a href="https://webaim.org/techniques/alttext/">more about alternative texts to images</a>. </p>
<p>If you add videos to the documentation, add captions and a transcript for the video. Many people tend to watch videos without a sound on, so it serves all. The Deaf, Hard of Hearing, and Deafblind depend on the captions and transcripts to know what's happening in the video. Here's a resource where you can <a href="https://webaim.org/techniques/captions/">learn more about captions</a>.</p>
<p>Also, it's good to pay attention to the structure of the content. Use headings correctly, meaning when you have a title, annotate it as such. Also, use correct heading levels. I just found a good tool for content creators to check some accessibility aspects, and I think it would work with documentation as well. The tool is called <a href="https://sa11y.netlify.app/">Sa11y</a>. Go and check it out!</p>
<h2 id="performance">Performance</h2>
<p>Another aspect to consider is performance. It is essential that the frontend does as few heavy calculations as possible. Not everyone has the latest devices with lots of memory and calculation power. </p>
<p>I know some people argue that this doesn't really matter. But it actually does. It matters for the user. If many things are happening in the front end, the user's internet is slow, and they have an older device, this might mean that nothing happens for them for a long time. That, in turn, means the site doesn't work for them.</p>
<p>If the site is something they can leave, they probably will. But what if they still need to use the site? Let's say it's the only way to book an appointment with a doctor, which they desperately need. If the site is slow and unresponsive, they might start trying to click things to see if it works. And when it finally starts working, all those things happen - and they find themselves in a totally wrong place. That's frustrating. But it can also be a huge barrier. Many don't just have the bandwidth needed to fight these non-working sites.</p>
<p>I want to remind you that many disabled people live in poverty. For example, in the United States, about 26% of disabled people lived in poverty in 2019. (Source: <a href="https://www.disabilitystatistics.org/reports/acs.cfm?statistic=7">Disability Statistics.</a>) They might not have the newest iPhone or the most high-performance computers. </p>
<p>And even if you don't live in poverty, it doesn't mean you'll always have the newest and most performant devices. I think this is often a bias we developers have - "Works on my computer" is so much more than being unable to reproduce a bug. It's also a privilege because we have our macbook pros and the newest phones to work with, and we build apps that work with them.</p>
<h2 id="backend-rendered-pages">Backend-rendered pages</h2>
<p>Another factor for the backend developers and accessibility is backend-rendered pages. They need to be accessible, too, like any client-side rendered pages. </p>
<p>Okay, I know; frontend developers handle the back-end-rendered pages in many cases. But there are always some times when backend devs need to create small pages. Those pages need to be accessible too. </p>
<p>So, even if you're a backend developer, I'd suggest looking into web accessibility and learning the basics. If you need to choose one topic to start with, I'd say semantic HTML. Not everything is a <code><div></code> - many other elements communicate meaning to assistive technology and work better. From there, I'd suggest continuing with keyboard navigation and other ways users use their devices - not everyone uses a mouse.</p>
<h2 id="wrapping-up">Wrapping Up</h2>
<p>So, in this blog post, I've been discussing ways backend developers can improve accessibility. There are four points I've been talking about: API structure, API documentation, performance, and backend rendered pages. </p>
<p>These are not the only ways a backend developer can (and why they should) contribute to accessibility. Do you have some ways in mind? I'd love to hear your thoughts!</p>
<h2 id="links">Links</h2>
<ul>
<li><a href="https://www.w3.org/WAI/fundamentals/accessibility-intro/">Web accessibility from Web Accessibility Initiative</a></li>
<li><a href="https://dev.to/eevajonnapanula/automated-accessibility-testing-is-a-good-start-but-you-need-to-test-manually-too-13f2">Use automated testing tools</a> </li>
<li><a href="https://eevis.codes/blog/2021-05-28/dont-develop-just-for-yourself-a-developers-checklist-to-accessibility/">Do some manual checks</a></li>
<li><a href="https://www.plainlanguage.gov/about/definitions/">Plain language</a></li>
<li><a href="https://www.disabilitystatistics.org/reports/acs.cfm?statistic=7">Disability Statistics</a></li>
<li><a href="https://webaim.org/techniques/alttext/">More about alternative texts to images</a></li>
<li><a href="https://webaim.org/techniques/captions/">Learn more about captions</a></li>
</ul>
</article>
On the Edge of Burnout... Again2022-07-15T00:00:00.000Zhttps://eevis.codes/blog/2022-07-15/on-the-edge-of-burnout-again/
<nav id="breadcrumbs" aria-label="breadcrumbs">
<ol>
<li>
<a href="https://eevis.codes/blog/">Blog</a>
<span aria-hidden="true" class="breadcrumb-separator">/<span>
</span></span></li>
<li>On the Edge of Burnout... Again</li><li>
</li></ol>
</nav>
<article class="blogPostLong">
<h1 id="/2022-07-15/on-the-edge-of-burnout-again">On the Edge of Burnout... Again</h1>
<div class="reading-time-and-date">
<p>
Reading time: about 5 minutes
</p>
<span class="separator"></span>
<p>Published <time datetime="15th Jul, 2022">15th Jul, 2022</time></p>
</div>
<section class="likes-and-boosts">
<div>
<svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="boosts-title">
<title id="boosts-title">Boosts</title>
<path d="M7.19 4.35047C5.15 6.64047 3.75 9.93047 3.62 10.2405L0 8.69047L4.05 4.64047C4.52 4.17047 5.2 3.96047 5.86 4.09047L7.19 4.35047ZM9.17 15.0005C9.17 15.0005 12.91 13.4505 15.06 11.3005C20.46 5.90047 19.56 1.68047 19.27 0.730473C18.32 0.430473 14.1 -0.459527 8.7 4.94047C6.55 7.09047 5 10.8305 5 10.8305L9.17 15.0005ZM15.65 12.8105C13.36 14.8505 10.07 16.2505 9.76 16.3805L11.31 20.0005L15.36 15.9505C15.83 15.4805 16.04 14.8005 15.91 14.1405L15.65 12.8105ZM7 16.0005C7 16.8305 6.66 17.5805 6.12 18.1205C4.94 19.3005 0 20.0005 0 20.0005C0 20.0005 0.7 15.0605 1.88 13.8805C2.2988 13.4595 2.83325 13.1724 3.4155 13.0558C3.99775 12.9392 4.60153 12.9982 5.15017 13.2253C5.69882 13.4525 6.16758 13.8376 6.49694 14.3317C6.8263 14.8258 7.0014 15.4067 7 16.0005ZM11 7.00047C11 5.90047 11.9 5.00047 13 5.00047C14.1 5.00047 15 5.90047 15 7.00047C15 8.10047 14.1 9.00047 13 9.00047C11.9 9.00047 11 8.10047 11 7.00047Z" fill="#00696D"></path>
</svg>
0
</div>
<div>
<svg role="img" width="20" height="19" viewBox="0 0 20 19" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="likes-title">
<title id="likes-title">Likes</title>
<path d="M10 18.35L8.55 17.03C3.4 12.36 0 9.27 0 5.5C0 2.41 2.42 0 5.5 0C7.24 0 8.91 0.81 10 2.08C11.09 0.81 12.76 0 14.5 0C17.58 0 20 2.41 20 5.5C20 9.27 16.6 12.36 11.45 17.03L10 18.35Z"></path>
</svg>
0
</div>
</section>
<img class="cover-photo" src="https://images.ctfassets.net/mpqufjsy02zr/7sSwdRJS4RixwXYHsXoM2s/f625a103ec91a68f2bad72eb07c14817/Twitter_post_-_10on-the-edge-of-burnout.png" alt="On the Edge of Burnout... Again." />
<div class="blog-tags">
<a href="https://eevis.codes/tags/career"><span class="blog-tag purple">career</span></a> <a href="https://eevis.codes/tags/a11y"><span class="blog-tag turquoise">a11y</span></a>
</div>
<nav aria-label="On the Edge of Burnout... Again Table of Contents" class="table-of-contents">
<h2>Table of Contents</h2>
<ol>
<li><a href="https://eevis.codes/blog/2022-07-15/on-the-edge-of-burnout-again/#im-better-now"> I'm better now</a></li><li><a href="https://eevis.codes/blog/2022-07-15/on-the-edge-of-burnout-again/#progress-over-perfection"> Progress over perfection</a></li><li><a href="https://eevis.codes/blog/2022-07-15/on-the-edge-of-burnout-again/#regaining-my-accessibility-spark"> Regaining my accessibility spark</a></li><li><a href="https://eevis.codes/blog/2022-07-15/on-the-edge-of-burnout-again/#wrapping-up"> Wrapping Up</a></li><li><a href="https://eevis.codes/blog/2022-07-15/on-the-edge-of-burnout-again/#links"> Links</a></li>
</ol>
</nav>
<p>I've been super tired during the spring. Again. I could write a blog post about how disappointed I am that I've let myself into this situation - again. But this blog post is not about it. It's about how I got further away from that edge and started finding myself - and the spark - again. </p>
<p>So, to give some context: About eight months ago, I wrote <a href="https://eevis.codes/blog/2021-11-22/first-month-as-an-accessibility-specialist-at-oura/">a blog post about my first month as an accessibility specialist at Oura</a>. At that point, I was still in the honeymoon phase, and the realities of being the first-ever accessibility specialist in the company weren't apparent. </p>
<p>You see, being the first in any role, especially in a role whose goal is purely to change existing things, is not easy. If there are any miscommunications about the position, it makes it even harder. And if you don't know what's expected of you, well, that's certainly a risk for burnout.</p>
<p>I should have known better - hey, I've been here before, and I know I'm more at risk because of my brain injury than someone else without the same history. </p>
<p>I realized I'd lost my spark. Not just for the work but also for anything outside the job. I used to love sharing what I've learned, but writing and submitting proposals for conferences didn't feel good. I thought, "What's the point? Nothing's going to change anyway". </p>
<p>Now that I'm in a better position, I'm a bit sad because I missed some conference applying deadlines I had been waiting for for a long time. For example, I didn't submit a proposal to Inclusive Design 24 - I just didn't have the energy. </p>
<h2 id="im-better-now">I'm better now</h2>
<p>I'm better now. But it took some time, conversations, and challenging moments before I could be honest about the situation. And again, I could use the word "again." I've been here before.</p>
<p>I tried to raise the problems throughout the spring. I think it was a short sickness leave that made the people at the company understand that this is pretty serious. </p>
<p>After that, we had multiple conversations about my role. I understood some miscommunications about my position. I had a pretty different idea of the role than it actually was. </p>
<p>Understanding what was expected of me and the whole situation better helped me. Furthermore, having some time off out in the sea helped. (I spent time sailing and kayaking. Finnish nature is amazing.)</p>
<p>I also read two blog posts that made me think a lot about my relationship with accessibility. I'll share my thoughts about them next.</p>
<h2 id="progress-over-perfection">Progress over perfection</h2>
<p>Working in a company, which is not very mature when it comes to accessibility, needs a specific type of attitude. Even though I knew that I couldn't (or can't) change everything instantly, I think I still had this idea of being able to change some visible things and doing it fast. And that was definitely a contributor to me being on the edge of burnout. </p>
<p>And I felt (and still feel) like I have responsibility. I'm the company's first (and the only) accessibility specialist, so I think I should be able to do something visible. To change something. Like how we don't add alternative texts to tweets, even the most important ones. </p>
<p>Meryl Evans has been talking about progress over perfection - and in her blog post, <a href="https://meryl.net/accessibility-progress-not-perfection/">"Accessibility: Why You Need to Work Toward Progress Over Perfection"</a>, she writes:</p>
<blockquote>
<p>Accessibility isn't all or nothing. It's progress, not perfection. </p>
</blockquote>
<p>I try to remember that. I try to find those good things, things that we do right. For example, we have some outstanding professionals who contribute to accessibility from their positions - for instance, Janne Käki, who does a fantastic job with iOS development. And that definitely is progress. </p>
<h2 id="regaining-my-accessibility-spark">Regaining my accessibility spark</h2>
<p>As mentioned earlier in the blog post, I felt like I had lost my (accessibility) spark. Everything felt more or less pointless. I felt that whatever I do, someone always tells me, "No, accessibility doesn't matter." Ok, usually, the words were not that direct. And it wasn't just about work - it was also outside it. </p>
<p>I read Sheri Bryne-Haber's blog post <a href="https://uxdesign.cc/regaining-your-accessibility-spark-a182eee9e8e6">"Regaining your accessibility spark"</a>. Initially, I thought it had some good advice. I didn't act on it. But I kept coming back to that post. </p>
<p>Some things on the list have helped me a lot this time. I needed to remind myself of the "why": why I do what I do. </p>
<p>I also needed (and still need) to remind myself about the progress, about the (little) things I've accomplished. Like the audit on our website and how the bug fixes from that audit are moving forward - they're not just visible yet. </p>
<p>And I sought help. I've had this incredible occupational psychologist with whom I've been talking about the work and how to improve it. To discuss with someone, to go through things with someone outside of the situation (but who knows the circumstances), has helped me tremendously. </p>
<p>Now that the situation is better, I have focused on things other than work. Sheri Bryne-Haber suggests things like finding accessibility mentoring/volunteering gigs, writing a blog or a book, or learning a new language, gaining a new hobby, or investing more in a hobby you already have. </p>
<p>And now that I think of it... I've done all three. I'm doing some volunteering. I've also been writing to my blog more (and I might have some ideas about the book part, too) and investing more in hobbies I already have - knitting and kayaking. As for investing, lately I've spent much more money on them than I have been for a while. But also time!</p>
<h2 id="wrapping-up">Wrapping Up</h2>
<p>So, I've been on the edge of burnout - again. In this blog post, I shared how I got further away from that edge and how I've been finding my accessibility spark again. It's been a journey, and that journey continues. </p>
<p>Do you have similar experiences? Did you lose your spark and regain it? </p>
<h2 id="links">Links</h2>
<ul>
<li><a href="https://eevis.codes/blog/2021-11-22/first-month-as-an-accessibility-specialist-at-oura/">A blog post about my first month as an accessibility specialist at Oura</a></li>
<li><a href="https://meryl.net/accessibility-progress-not-perfection/">"Accessibility: Why You Need to Work Toward Progress Over Perfection"</a></li>
<li><a href="https://uxdesign.cc/regaining-your-accessibility-spark-a182eee9e8e6">"Regaining your accessibility spark"</a></li>
</ul>
</article>
My Advice to a Developer New to Accessibility2022-07-22T00:00:00.000Zhttps://eevis.codes/blog/2022-07-22/my-advice-to-a-developer-new-to-accessibility/
<nav id="breadcrumbs" aria-label="breadcrumbs">
<ol>
<li>
<a href="https://eevis.codes/blog/">Blog</a>
<span aria-hidden="true" class="breadcrumb-separator">/<span>
</span></span></li>
<li>My Advice to a Developer New to Accessibility</li><li>
</li></ol>
</nav>
<article class="blogPostLong">
<h1 id="/2022-07-22/my-advice-to-a-developer-new-to-accessibility">My Advice to a Developer New to Accessibility</h1>
<div class="reading-time-and-date">
<p>
Reading time: about 7 minutes
</p>
<span class="separator"></span>
<p>Published <time datetime="22nd Jul, 2022">22nd Jul, 2022</time></p>
</div>
<section class="likes-and-boosts">
<div>
<svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="boosts-title">
<title id="boosts-title">Boosts</title>
<path d="M7.19 4.35047C5.15 6.64047 3.75 9.93047 3.62 10.2405L0 8.69047L4.05 4.64047C4.52 4.17047 5.2 3.96047 5.86 4.09047L7.19 4.35047ZM9.17 15.0005C9.17 15.0005 12.91 13.4505 15.06 11.3005C20.46 5.90047 19.56 1.68047 19.27 0.730473C18.32 0.430473 14.1 -0.459527 8.7 4.94047C6.55 7.09047 5 10.8305 5 10.8305L9.17 15.0005ZM15.65 12.8105C13.36 14.8505 10.07 16.2505 9.76 16.3805L11.31 20.0005L15.36 15.9505C15.83 15.4805 16.04 14.8005 15.91 14.1405L15.65 12.8105ZM7 16.0005C7 16.8305 6.66 17.5805 6.12 18.1205C4.94 19.3005 0 20.0005 0 20.0005C0 20.0005 0.7 15.0605 1.88 13.8805C2.2988 13.4595 2.83325 13.1724 3.4155 13.0558C3.99775 12.9392 4.60153 12.9982 5.15017 13.2253C5.69882 13.4525 6.16758 13.8376 6.49694 14.3317C6.8263 14.8258 7.0014 15.4067 7 16.0005ZM11 7.00047C11 5.90047 11.9 5.00047 13 5.00047C14.1 5.00047 15 5.90047 15 7.00047C15 8.10047 14.1 9.00047 13 9.00047C11.9 9.00047 11 8.10047 11 7.00047Z" fill="#00696D"></path>
</svg>
0
</div>
<div>
<svg role="img" width="20" height="19" viewBox="0 0 20 19" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="likes-title">
<title id="likes-title">Likes</title>
<path d="M10 18.35L8.55 17.03C3.4 12.36 0 9.27 0 5.5C0 2.41 2.42 0 5.5 0C7.24 0 8.91 0.81 10 2.08C11.09 0.81 12.76 0 14.5 0C17.58 0 20 2.41 20 5.5C20 9.27 16.6 12.36 11.45 17.03L10 18.35Z"></path>
</svg>
0
</div>
</section>
<img class="cover-photo" src="https://images.ctfassets.net/mpqufjsy02zr/1lELvZGBKzLv3PrJkUSC82/4f8b9a4d56595a63f1e33d9aa49404de/Twitter_post_-_11solving-non-coding-problems.png" alt="" />
<div class="blog-tags">
<a href="https://eevis.codes/tags/disability"><span class="blog-tag purple">disability</span></a> <a href="https://eevis.codes/tags/a11y"><span class="blog-tag turquoise">a11y</span></a> <a href="https://eevis.codes/tags/webdev"><span class="blog-tag blue">webdev</span></a>
</div>
<nav aria-label="My Advice to a Developer New to Accessibility Table of Contents" class="table-of-contents">
<h2>Table of Contents</h2>
<ol>
<li><a href="https://eevis.codes/blog/2022-07-22/my-advice-to-a-developer-new-to-accessibility/#learn-semantic-html-and-use-it"> Learn Semantic HTML and Use It</a></li><li><a href="https://eevis.codes/blog/2022-07-22/my-advice-to-a-developer-new-to-accessibility/#learn-about-keyboard-navigation"> Learn About Keyboard Navigation</a></li><li><a href="https://eevis.codes/blog/2022-07-22/my-advice-to-a-developer-new-to-accessibility/#listen-to-people-with-disabilities"> Listen to People with Disabilities</a></li><li><a href="https://eevis.codes/blog/2022-07-22/my-advice-to-a-developer-new-to-accessibility/#links-in-the-blog-post"> Links in the Blog Post</a></li>
</ol>
</nav>
<p>Learning about accessibility for the first time can feel overwhelming. You start reading, and at some point, it hits you: There is so much to learn! And if you're like me and feel that it's your responsibility to make the things you create accessible, that might even make you feel anxious. </p>
<p>And yes, there is a lot to learn regarding accessibility. But you don't need to know everything at once. As Meryl Evans reminds us in her tweet (and a blog post the tweet links to), it's about progress over perfection: </p>
<p><a href="https://twitter.com/merylkevans/status/1547635550466674692">https://twitter.com/merylkevans/status/1547635550466674692</a></p>
<p>So, here are some things I wish I had known when I first started learning accessibility. Heck, I wish I had known these things when I started learning web development! I'll share semantic HTML, keyboard navigation, and listening to people with disabilities. </p>
<h2 id="learn-semantic-html-and-use-it">Learn Semantic HTML and Use It</h2>
<p>So, first thing: Learn semantic HTML. But what does it mean? Semantic HTML, or semantic markup, describes its meaning to the browser and developer in a human- and machine-readable way. So, with semantic elements, a human will know what the HTML element is about, and the browser knows what it should render and how it should behave when a user interacts with it.</p>
<p>Here's an example: </p>
<pre><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span> <span class="token special-attr"><span class="token attr-name">onClick</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token value javascript language-javascript"><span class="token punctuation">{</span><span class="token spread operator">...</span><span class="token punctuation">}</span></span></span></span><span class="token punctuation">></span></span>I'm an honest button<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span>
</code></pre><p>That button uses a semantic <code>button</code>-element. When it does so, the browser knows what it should do when the user either clicks or activates it with a keyboard or other input device. Here's an example of a non-semantic "button":</p>
<pre><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>button<span class="token punctuation">"</span></span> <span class="token special-attr"><span class="token attr-name">onClick</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token value javascript language-javascript"><span class="token punctuation">{</span><span class="token spread operator">...</span><span class="token punctuation">}</span></span></span></span><span class="token punctuation">></span></span>
I look like a button
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span>
</code></pre><p>So, this "button" made out of <code>div</code> looks like a button and even has the <code>onClick</code>-handler. So, wouldn't a human recognize it as a button? Well, yes and no. It depends on the human. </p>
<p>You see, this "button" works only for mouse users. As <code>div</code> is not an interactive element by nature, it doesn't handle interaction the same way as, for example, a <code>button</code>-element. When using a semantic button-element, that onClick-event propagates such that it handles keyboard interaction as well. However, <code>div</code>s don't do such a thing.</p>
<p>Another reason a non-mouse user will recognize that it's not a button is that you can't focus on it. That means the button is not in the focus/tab order. And that, in turn, means that a non-mouse user can't interact with it. </p>
<p>There are lots of semantic elements in the HTML. For some reason, we tend to use a <code>div</code> for many things. Did you know, for example, that you can annotate addresses with an <code>address</code>-element? Or wrap dates or times with the <code>time</code>-element? Or display progress with a <code>progress</code>-element? <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element#embedded_content">MDN lists HTML elements</a>, so be sure to check that list. </p>
<p>If you want to read about other benefits of using semantic HTML, I wrote a blog post <a href="https://dev.to/eevajonnapanula/ode-to-semantic-html-38c3">"Ode to Semantic HTML"</a> a while back. </p>
<h2 id="learn-about-keyboard-navigation">Learn About Keyboard Navigation</h2>
<p>Another piece of advice I want to give is something we touched on in the previous section: Learn about keyboard navigation.</p>
<h3 id="what-and-why-of-keyboard-navigation">What And Why of Keyboard Navigation</h3>
<p>Now you might wonder, "Why would anyone use keyboard to navigate?". Well, first of all: not everyone can use a mouse, or they just don't want to. Sometimes doing tasks on a computer is way faster with, for example, a keyboard. </p>
<p>There are lots of different ways of using digital devices. Some people use a tool called a screen reader, which reads the screen's contents out loud. Some use a keyboard for navigation because they can't use a mouse. And some people utilize assistive tools like mouth sticks, custom keyboards, switch devices, and other tools to use digital devices. And let's not forget voice interfaces or eye-tracking technologies, which some people use.</p>
<p>So the point is that there are multiple other ways to interact with a digital device than just a mouse. And we, as developers, need to create interfaces that work for all of our users and not exclude anyone.</p>
<p>Now you might feel overwhelmed. "How can I create websites for all those needs?" Well, I have good news: You don't need to think about each different input method. Many of the input methods mentioned above emulate what a plain ol' keyboard does. </p>
<p>My suggestion is: first, learn how keyboard navigation should work on a website. After you've learned that, it's time to learn about the specifics of other input methods.</p>
<p>Let's talk a bit more about keyboard interaction. There are two things I urge you to learn and exercise: Keyboard navigation patterns and how to test them (and regularly do so).</p>
<h3 id="keyboard-navigation-patterns">Keyboard Navigation Patterns</h3>
<p>So, how does keyboard navigation work? When users want to go forward, they press the <kbd>Tab</kbd>-key, and this takes (or should take, depending on how well the website has been coded) them to the next interactive element. That means a link, button, input field, or similar - and not, for example, a heading.</p>
<p>When keyboard users want to read something or to navigate forward on a site without interactive elements, they scroll with arrow keys. I want to emphasize that because I see a lot of websites where a developer has added <code>tabIndex</code>-attribute to non-interactive elements so keyboard users could navigate forward. </p>
<p>It's a great thing that a developer thinks about keyboard users. This pattern, however, increases the amount of the tab stops to reach the actual interactive elements. For some, it's frustrating, but for some, it can even be painful if, for example, each keypress causes pain.</p>
<p>Okay, back to patterns. When a keyboard user wants to navigate to the previous interactive element, they use <kbd>Shift</kbd> + <kbd>Tab</kbd>. </p>
<p>There are different patterns for activating interactive elements. I'll list a few of them:</p>
<ul>
<li><code><button></code>: activates with <kbd>Enter</kbd> or <kbd>Space</kbd></li>
<li><code><a></code>: activates with <kbd>Enter</kbd></li>
<li><code><input type="checkbox" /></code>: toggles with <kbd>Space</kbd></li>
<li><code><input type="radio" /></code>: <kbd>Tab</kbd> in (and out) the radio input group, arrow-keys to change the value.</li>
</ul>
<p>As you can see, buttons and links are activated with different keys. Specifically, the spacebar key doesn't activate a link. This distinction between the elements is good to remember - when users see something like a button, they think it works as a button. If the underlying component is a link, it doesn't activate with <kbd>Space</kbd> but rather scrolls down on the page. That's super frustrating.</p>
<p>You can find the expected keyboard navigation patterns for more complex user interface element patterns from <a href="https://www.w3.org/WAI/ARIA/apg/patterns/">WAI-ARIA Authoring practices</a>.</p>
<h3 id="testing-with-keyboard">Testing with Keyboard</h3>
<p>When you build user interfaces, be sure to test with a keyboard. The basic method is to navigate through the whole site using the keyboard. When you encounter an interactive element, try to trigger it. Check that it works as expected - meaning that you can do everything with a keyboard that you can with a mouse. </p>
<p>Oh, and one note if you're using Mac and Safari. You need to enable keyboard navigation for them - I don't know why, but it doesn't work out of the box. Here are <a href="https://www.a11yproject.com/posts/macos-browser-keyboard-navigation/">instructions for enabling keyboard navigation for Mac and Safari.</a></p>
<h2 id="listen-to-people-with-disabilities">Listen to People with Disabilities</h2>
<p>The final advice I will give in this blog post is to listen to disabled people. This advice consists of two things: Listening and learning about their (our) life in general and listening to the feedback we/they give. </p>
<p>I'll share some resources I've found helpful and informative. There's plenty more out there on the internet, and if you think something should be included, I definitely want to know about them!</p>
<p>One of the good places to go for these stories is Twitter. There are a couple of hashtags I recommend following:
- <a href="https://twitter.com/search?q=actuallyautistic&src=typed_query&f=live">#ActuallyAutistic</a>
- <a href="https://twitter.com/hashtag/EverydayAbleism?src=hashtag_click&f=live">#EverydayAbleism</a>
- <a href="https://twitter.com/hashtag/DisabilityPrideMonth?src=hashtag_click&f=live">#DisabilityPrideMonth</a></p>
<p>I'd love to add some more here, so if you know some other good hashtags, let me know.</p>
<p>Also, a good resource for experiences and expectations of disabled people is <a href="https://a11yrules.com/series/a11y-rules-soundbite/">Nicolas Steenhouts' A11y Rules Soundbite-podcast series</a>. These short discussions with disabled people give a lot of good points of view for designing and building the web. </p>
<p>I also want to mention a brilliant book from <a href="https://emilyladau.com/book/">Emily Ladau: Demystifying Disability</a>. I actually wrote some thoughts from it once I had read it, so check that blog post too: <a href="https://eevis.codes/blog/2021-11-06/thoughts-from-reading-demystifying-disability-by-emily-ladau/">"Thoughts From Reading Demystifying Disability by Emily Ladau"</a></p>
<p>Another great resource is <a href="https://disabilityvisibilityproject.com/">Disability Visibility-project</a>. It's defined on the About-page with the following words:</p>
<blockquote>
<p>The Disability Visibility Project is an online community dedicated to creating, sharing, and amplifying disability media and culture.</p>
</blockquote>
<p>Do you have some other resources? Or other advice an accessibility-novice developer should know about? </p>
<h2 id="links-in-the-blog-post">Links in the Blog Post</h2>
<ul>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element#embedded_content">MDN lists HTML elements</a></li>
<li><a href="https://dev.to/eevajonnapanula/ode-to-semantic-html-38c3">"Ode to Semantic HTML"</a></li>
<li><a href="https://www.w3.org/WAI/ARIA/apg/patterns/">WAI-ARIA Authoring practices</a></li>
<li><a href="https://www.a11yproject.com/posts/macos-browser-keyboard-navigation/">Instructions for enabling keyboard navigation for Mac and Safari.</a></li>
<li><a href="https://twitter.com/search?q=actuallyautistic&src=typed_query&f=live">#ActuallyAutistic</a></li>
<li><a href="https://twitter.com/hashtag/EverydayAbleism?src=hashtag_click&f=live">#EverydayAbleism</a></li>
<li><a href="https://twitter.com/hashtag/DisabilityPrideMonth?src=hashtag_click&f=live">#DisabilityPrideMonth</a></li>
<li><a href="https://a11yrules.com/series/a11y-rules-soundbite/">Nicolas Steenhouts' A11y Rules Soundbite-podcast series</a>.</li>
<li><a href="https://emilyladau.com/book/">Emily Ladau: Demystifying Disability</a></li>
<li><a href="https://eevis.codes/blog/2021-11-06/thoughts-from-reading-demystifying-disability-by-emily-ladau/">"# Thoughts From Reading Demystifying Disability by Emily Ladau"</a></li>
<li><a href="https://disabilityvisibilityproject.com/">Disability Visibility-project</a></li>
</ul>
</article>
Don't Set outline: 0 or outline: none for Focus-Styles2022-07-28T00:00:00.000Zhttps://eevis.codes/blog/2022-07-28/dont-set-outline-0-or-outline-none-for-focus/
<nav id="breadcrumbs" aria-label="breadcrumbs">
<ol>
<li>
<a href="https://eevis.codes/blog/">Blog</a>
<span aria-hidden="true" class="breadcrumb-separator">/<span>
</span></span></li>
<li>Don't Set outline: 0 or outline: none for Focus-Styles</li><li>
</li></ol>
</nav>
<article class="blogPostLong">
<h1 id="/2022-07-28/dont-set-outline-0-or-outline-none-for-focus">Don't Set outline: 0 or outline: none for Focus-Styles</h1>
<div class="reading-time-and-date">
<p>
Reading time: about 4 minutes
</p>
<span class="separator"></span>
<p>Published <time datetime="28th Jul, 2022">28th Jul, 2022</time></p>
</div>
<section class="likes-and-boosts">
<div>
<svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="boosts-title">
<title id="boosts-title">Boosts</title>
<path d="M7.19 4.35047C5.15 6.64047 3.75 9.93047 3.62 10.2405L0 8.69047L4.05 4.64047C4.52 4.17047 5.2 3.96047 5.86 4.09047L7.19 4.35047ZM9.17 15.0005C9.17 15.0005 12.91 13.4505 15.06 11.3005C20.46 5.90047 19.56 1.68047 19.27 0.730473C18.32 0.430473 14.1 -0.459527 8.7 4.94047C6.55 7.09047 5 10.8305 5 10.8305L9.17 15.0005ZM15.65 12.8105C13.36 14.8505 10.07 16.2505 9.76 16.3805L11.31 20.0005L15.36 15.9505C15.83 15.4805 16.04 14.8005 15.91 14.1405L15.65 12.8105ZM7 16.0005C7 16.8305 6.66 17.5805 6.12 18.1205C4.94 19.3005 0 20.0005 0 20.0005C0 20.0005 0.7 15.0605 1.88 13.8805C2.2988 13.4595 2.83325 13.1724 3.4155 13.0558C3.99775 12.9392 4.60153 12.9982 5.15017 13.2253C5.69882 13.4525 6.16758 13.8376 6.49694 14.3317C6.8263 14.8258 7.0014 15.4067 7 16.0005ZM11 7.00047C11 5.90047 11.9 5.00047 13 5.00047C14.1 5.00047 15 5.90047 15 7.00047C15 8.10047 14.1 9.00047 13 9.00047C11.9 9.00047 11 8.10047 11 7.00047Z" fill="#00696D"></path>
</svg>
0
</div>
<div>
<svg role="img" width="20" height="19" viewBox="0 0 20 19" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="likes-title">
<title id="likes-title">Likes</title>
<path d="M10 18.35L8.55 17.03C3.4 12.36 0 9.27 0 5.5C0 2.41 2.42 0 5.5 0C7.24 0 8.91 0.81 10 2.08C11.09 0.81 12.76 0 14.5 0C17.58 0 20 2.41 20 5.5C20 9.27 16.6 12.36 11.45 17.03L10 18.35Z"></path>
</svg>
0
</div>
</section>
<img class="cover-photo" src="https://images.ctfassets.net/mpqufjsy02zr/31C2z6kWyHsyO2m39XI8bM/daf07c5464a813c8cb47304611eeae70/Twitter_post_-_12solving-non-coding-problems__1_.png" alt="Don't Set `outline: 0` or `outline: none` for Focus." />
<div class="blog-tags">
<a href="https://eevis.codes/tags/CSS"><span class="blog-tag blue">CSS</span></a> <a href="https://eevis.codes/tags/webdev"><span class="blog-tag blue">webdev</span></a> <a href="https://eevis.codes/tags/a11y"><span class="blog-tag turquoise">a11y</span></a>
</div>
<nav aria-label="Don't Set outline: 0 or outline: none for Focus-Styles Table of Contents" class="table-of-contents">
<h2>Table of Contents</h2>
<ol>
<li><a href="https://eevis.codes/blog/2022-07-28/dont-set-outline-0-or-outline-none-for-focus/#what-is-outline"> What is <code>outline</code>?</a></li><li><a href="https://eevis.codes/blog/2022-07-28/dont-set-outline-0-or-outline-none-for-focus/#outline-and-focus"> <code>outline</code> and Focus</a></li><li><a href="https://eevis.codes/blog/2022-07-28/dont-set-outline-0-or-outline-none-for-focus/#why-visible-focus-styles-are-important"> Why Visible Focus Styles Are Important?</a></li><li><a href="https://eevis.codes/blog/2022-07-28/dont-set-outline-0-or-outline-none-for-focus/#but-what-if-i-have-better-focus-styles"> But What If I Have Better Focus Styles?</a></li><li><a href="https://eevis.codes/blog/2022-07-28/dont-set-outline-0-or-outline-none-for-focus/#wrapping-up"> Wrapping Up</a></li><li><a href="https://eevis.codes/blog/2022-07-28/dont-set-outline-0-or-outline-none-for-focus/#links"> Links </a></li>
</ol>
</nav>
<p>"The focus indicator is ugly, and I will remove it. No, this is not up for discussion." These are some lines from a conversation with a designer-developer some years back. The theme of the conversation was a website they were working with.</p>
<p>I tried to explain the problem with non-existing focus styles. Still, they insisted their view of beauty was more important than the customer's right to a working website. </p>
<p>And this has not been the only conversation I've had around focus styles. Some people want, stubbornly, to remove all focus styles because they (as a mouse user) don't like those focus rings.</p>
<p>In this blog post, I'll share why these visible focus styles are so important - and why you should never set <code>outline</code>-property to <code>0</code> or <code>none</code> for focus styles. But let's first talk about the property itself. </p>
<h2 id="what-is-outline">What is <code>outline</code>?</h2>
<p><code>outline</code> is a CSS property, or actually, a CSS shorthand property. That means that you can set multiple properties with it. These properties are <code>outline-color</code>, <code>outline-style</code>, and <code>outline-width.</code> You can set one, two, or three attributes simultaneously. Here's an example:</p>
<pre><code class="language-css"><span class="token selector"><span class="token class">.className</span></span> <span class="token punctuation">{</span>
<span class="token property">outline</span><span class="token punctuation">:</span> <span class="token number">2</span><span class="token unit">px</span> solid <span class="token color">red</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre><p>In this example, the outline is set to be a solid red line, which is 2px wide.</p>
<p>In the box model, <code>outline</code> is set <em>outside</em> the box's border edge and does not add to the element's size, meaning it doesn't take any space from the page layout.</p>
<p>You can modify the looks of the outline with two more properties: <code>outline-offset</code>, which affects how far from the border of an element the outline is, and <code>border-radius</code>, which modifies the radius of the border. It affects the whole element, not just outline.</p>
<p>You can <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/outline">read more about the <code>outline</code> in MDN.</a></p>
<h2 id="outline-and-focus"><code>outline</code> and Focus</h2>
<p>The default styles for focus indicators are implemented with the <code>outline</code>-property. Every browser has its own default styles for this focus indicator.</p>
<p>These default styles are defined with user agent stylesheets. Jens Oliver Meier has written more about them, if you're interested: <a href="https://meiert.com/en/blog/user-agent-style-sheets/">"User Agent Style Sheets: Basics and Samples"</a>. </p>
<p>Here are examples of default focus indicators for Chrome, Firefox, and Safari, as seen on Mac:</p>
<p>Chrome:</p>
<p><img src="https://images.ctfassets.net/sk24pdnnlwhc/4F7kQwDJXFg3k9UE0h9MC9/2c180877e24f5a724f394d785ed674e2/Screenshot_2022-07-28_at_6.28.45.png" alt="Link with text "Beauty of Finland's national parks still drawing crowds after pandemic peaks" in blue, and a darker blue outline surrounding the text." /></p>
<p>Firefox:</p>
<p><img src="https://images.ctfassets.net/sk24pdnnlwhc/7vgr80VHtXe4tIKrBrLJhY/8c1da50d8d8395f1eb88360526c69351/Screenshot_2022-07-28_at_6.30.56.png" alt="Link with text "Beauty of Finland's national parks still drawing crowds after pandemic peaks" in blue in two rows, and a semi-blue outline surrounding each of the lines." /></p>
<p>Safari:</p>
<p><img src="https://images.ctfassets.net/sk24pdnnlwhc/5cjC6GFzzlMpOnJCDRZSrw/7d2d4721f93a8e6c1cedbb01b539cdf5/Screenshot_2022-07-28_at_6.33.10.png" alt="Link with text "Beauty of Finland's national parks still drawing crowds after pandemic peaks" in blue, and a light blue outline surrounding the text. The outline has a bit of padding between it and the text." /></p>
<p>The examples are from the Finnish Broadcasting Company's (Yle) website. I've disabled the author styles with the Web Developer extension. </p>
<p>As you might notice from the examples, these styles aren't apparent in most conditions. The default focus indicator styles do pass WCAG criteria about visible focus. Still, I think accessibility shouldn't be just about passing the success criteria. It should be about inclusion, and thus creating more visible focus styles is a must. </p>
<h2 id="why-visible-focus-styles-are-important">Why Visible Focus Styles Are Important?</h2>
<p>So, why having visible focus styles is so important? It's because not everyone uses a mouse. Many people prefer or need to use tools like keyboards, switch devices, or others to navigate the page. And when they do so, they don't have the cursor of a mouse to tell them where they are on the page - they rely on focus styles.</p>
<p>So, to put it short - not having visible focus styles is like using a website with a mouse, but the cursor is invisible. </p>
<h2 id="but-what-if-i-have-better-focus-styles">But What If I Have Better Focus Styles?</h2>
<p>The outline completely disappears when you set <code>outline</code> to none or 0. Now you might ask, "But what if I have better focus styles? Why can't I remove the outline then?" </p>
<p>The problem is that it removes the outline from everywhere - also, from, for example, Windows High Contrast Mode (WHCM) users. WHCM works by removing the background colors and images and replacing text color (and some other colors) with the selected theme's colors. That affects things like <code>box-shadow</code> - it's not visible at all. And that, in turn, means that most of the enhanced focus styles won't appear.</p>
<p>"So, I'm stuck with a visible outline, then?" I have good news: No, you're not! You can actually use the <code>transparent</code>-keyword like this:</p>
<pre><code class="language-css"><span class="token selector"><span class="token class">.some-element</span><span class="token pseudo-class">:focus</span></span> <span class="token punctuation">{</span>
<span class="token property">outline</span><span class="token punctuation">:</span> <span class="token number">1</span><span class="token unit">px</span> solid <span class="token color">transparent</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre><p>That will show up in WHCM as it forces the colors on existing outlines and will be transparent in other cases. And also, because the outline doesn't take up space on the boxes, the transparent outline will be invisible in other cases.</p>
<h2 id="wrapping-up">Wrapping Up</h2>
<p>So, the gist of this blog post is: never set <code>outline</code>-property to <code>0</code> or <code>none</code> for focus styles, use the <code>transparent</code>-keyword for the color in these cases. And if you do this, remember to add (more) visible focus styles via the chosen alternative method.</p>
<h2 id="links">Links</h2>
<ul>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/CSS/outline">Read more about the <code>outline</code> in MDN</a></li>
<li><a href="https://meiert.com/en/blog/user-agent-style-sheets/">"User Agent Style Sheets: Basics and Samples"</a></li>
</ul>
</article>
Notes of a Web Developer Learning Kotlin and Android Development - part 12022-08-02T00:00:00.000Zhttps://eevis.codes/blog/2022-08-02/notes-of-a-web-developer-learning-kotlin-and-android-development-part-1/
<nav id="breadcrumbs" aria-label="breadcrumbs">
<ol>
<li>
<a href="https://eevis.codes/blog/">Blog</a>
<span aria-hidden="true" class="breadcrumb-separator">/<span>
</span></span></li>
<li>Notes of a Web Developer Learning Kotlin and Android Development - part 1</li><li>
</li></ol>
</nav>
<article class="blogPostLong">
<h1 id="/2022-08-02/notes-of-a-web-developer-learning-kotlin-and-android-development-part-1">Notes of a Web Developer Learning Kotlin and Android Development - part 1</h1>
<div class="reading-time-and-date">
<p>
Reading time: about 5 minutes
</p>
<span class="separator"></span>
<p>Published <time datetime="2nd Aug, 2022">2nd Aug, 2022</time></p>
</div>
<section class="likes-and-boosts">
<div>
<svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="boosts-title">
<title id="boosts-title">Boosts</title>
<path d="M7.19 4.35047C5.15 6.64047 3.75 9.93047 3.62 10.2405L0 8.69047L4.05 4.64047C4.52 4.17047 5.2 3.96047 5.86 4.09047L7.19 4.35047ZM9.17 15.0005C9.17 15.0005 12.91 13.4505 15.06 11.3005C20.46 5.90047 19.56 1.68047 19.27 0.730473C18.32 0.430473 14.1 -0.459527 8.7 4.94047C6.55 7.09047 5 10.8305 5 10.8305L9.17 15.0005ZM15.65 12.8105C13.36 14.8505 10.07 16.2505 9.76 16.3805L11.31 20.0005L15.36 15.9505C15.83 15.4805 16.04 14.8005 15.91 14.1405L15.65 12.8105ZM7 16.0005C7 16.8305 6.66 17.5805 6.12 18.1205C4.94 19.3005 0 20.0005 0 20.0005C0 20.0005 0.7 15.0605 1.88 13.8805C2.2988 13.4595 2.83325 13.1724 3.4155 13.0558C3.99775 12.9392 4.60153 12.9982 5.15017 13.2253C5.69882 13.4525 6.16758 13.8376 6.49694 14.3317C6.8263 14.8258 7.0014 15.4067 7 16.0005ZM11 7.00047C11 5.90047 11.9 5.00047 13 5.00047C14.1 5.00047 15 5.90047 15 7.00047C15 8.10047 14.1 9.00047 13 9.00047C11.9 9.00047 11 8.10047 11 7.00047Z" fill="#00696D"></path>
</svg>
0
</div>
<div>
<svg role="img" width="20" height="19" viewBox="0 0 20 19" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="likes-title">
<title id="likes-title">Likes</title>
<path d="M10 18.35L8.55 17.03C3.4 12.36 0 9.27 0 5.5C0 2.41 2.42 0 5.5 0C7.24 0 8.91 0.81 10 2.08C11.09 0.81 12.76 0 14.5 0C17.58 0 20 2.41 20 5.5C20 9.27 16.6 12.36 11.45 17.03L10 18.35Z"></path>
</svg>
0
</div>
</section>
<img class="cover-photo" src="https://images.ctfassets.net/mpqufjsy02zr/5VbETfRk638r5exCu5Effp/86001841e80928ddb9ee5c0463b65057/Kotlin-Android-part1on-the-edge-of-burnout.png" alt="Notes of a Web Developer Learning Kotlin and Android Development - part 1." />
<div class="blog-tags">
<a href="https://eevis.codes/tags/backend"><span class="blog-tag turquoise">backend</span></a> <a href="https://eevis.codes/tags/Kotlin"><span class="blog-tag turquoise">Kotlin</span></a>
</div>
<nav aria-label="Notes of a Web Developer Learning Kotlin and Android Development - part 1 Table of Contents" class="table-of-contents">
<h2>Table of Contents</h2>
<ol>
<li><a href="https://eevis.codes/blog/2022-08-02/notes-of-a-web-developer-learning-kotlin-and-android-development-part-1/#first-impressions-from-kotlin"> First Impressions from Kotlin</a></li><li><a href="https://eevis.codes/blog/2022-08-02/notes-of-a-web-developer-learning-kotlin-and-android-development-part-1/#getting-back-to-android-development"> Getting Back to Android Development</a></li><li><a href="https://eevis.codes/blog/2022-08-02/notes-of-a-web-developer-learning-kotlin-and-android-development-part-1/#wrapping-up"> Wrapping Up</a></li><li><a href="https://eevis.codes/blog/2022-08-02/notes-of-a-web-developer-learning-kotlin-and-android-development-part-1/#links"> Links</a></li>
</ol>
</nav>
<p>First, a bit of background: I'm an accessibility specialist who recently started digging deeper into our company's Android application. When auditing a web page, it's easy for me to provide technical guidance on how to fix accessibility issues. However, native mobile development and accessibility have been a mystery to me. Now that I've started evaluating our native apps, I've realized I need to understand more from the technical side. </p>
<p>Okay, I have to admit that I'm not a complete newbie in Android development. When I was learning to code about 6-7 years ago, one of the learning paths I was following was Android development. I was working through Google's Android courses in Udacity back then but never finished them. </p>
<p>At the time, the language for Android development was Java, and I've never felt too comfortable with it. I ended up working as a web developer (one of the other learning paths I was taking) and have been able to avoid Java ever since. Fortunately, today Java is more in the background, and Kotlin seems to be the hot thing, so I decided to learn it with Android development. </p>
<p>In this and upcoming blog posts, I will share thoughts, learnings, and reflections from learning Kotlin and Android development. My initial plan is to work through the <a href="https://developer.android.com/courses/android-basics-kotlin/course">Android Basics in Kotlin</a> and, after that, learn more about Android accessibility specifically. </p>
<p>Let's start with the first unit (Kotlin Basics). It is about the basics of Kotlin and Android - adding text and images and creating a simple, interactive interface. </p>
<h2 id="first-impressions-from-kotlin">First Impressions from Kotlin</h2>
<p>I admit I've been a bit prejudiced towards Kotlin because many people have been selling it to me with the idea that it's like "better Java." I've never particularly liked Java. </p>
<p>However, now that I started learning Kotlin, I'm feeling like... Wow, why haven't I tried this before? For me, it resembles Python a bit. I can't exactly pinpoint why, but I get this same soft feeling when coding in Python.</p>
<p>Okay, I do just some dabbling with Python for fun, and I'm rarely working with it, but I really like it. So I can wholeheartedly say that the first impressions from Kotlin were positive. </p>
<p>I know that part of feeling like this is because I've been coding for a long time now, and I've learned at least the basics of many languages. I know JS, TS, Python, Java, Clojure, and Ruby, to name a few. And learning new (programming) languages gets easier after every new language.</p>
<p>Let's look at some concrete learnings and findings from Kotlin from the first unit. I collected the topics from the first unit and did some extra reading to understand some differences and concepts. </p>
<h3 id="variable-names">Variable Names</h3>
<p>The first thing to learn with a new language is usually variables and what's their syntax. I collected three keywords from the first unit to define variables: <code>val</code>, <code>var</code>, and <code>const.</code></p>
<dl>
<dt><code>val</code></dt>
<dd><code>val</code> is used to define read-only, local variables. </dd>
<dt><code>var</code></dt>
<dd><code>var</code> is used to define mutable variables.</dd>
<dt><code>const</code></dt>
<dd><code>const</code> is used to define immutable values (the same as "val"). The difference is that these variables are known at compile time. An example from the course: <code>TAG</code> was used to define the component name when using <code>Logger</code>, and was set to <code>MainActivity</code>.</dd>
</dl>
<p>Another important keyword is <code>fun</code>, which defines functions. An example:</p>
<pre><code class="language-kotlin"><span class="token keyword">fun</span> <span class="token function">petACat</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token comment">// ...</span>
<span class="token punctuation">}</span>
</code></pre><p>That is straightforward, as JavaScript has a similar syntax with the <code>function</code>-keyword. I just need to remember the different naming.</p>
<h3 id="some-nice-methods-and-data-types">Some Nice Methods and Data Types</h3>
<p>The course went through some interesting and useful data types (<code>range</code>) and methods (<code>random</code>, <code>when</code>-statement, and <code>repeat</code>). I know that most, or all, are present in the other languages I mentioned learning. However, I'm working primarily with JavaScript, so that's what I'm comparing Kotlin to within my mind. </p>
<h4 id="ranges-and-random-method">Ranges and Random-Method</h4>
<p>One of the apps created on the course was a dice-throwing app. The functionality is simple: a user clicks a button, and the app shows a random number between 1 and 6. </p>
<p>To accomplish this, the code uses a range to create a set of possible values (as the values are in a range between 1 and 6). And that range (<code>IntRange</code>) has a method called <code>random</code>, which, as you would guess, returns a random number that is on that range. </p>
<p>It's so much more straightforward than in JavaScript. I like it a lot. Here's an example:</p>
<pre><code class="language-kotlin"><span class="token keyword">val</span> range <span class="token operator">=</span> <span class="token number">1</span><span class="token operator">..</span><span class="token number">6</span>
<span class="token keyword">val</span> randomNum <span class="token operator">=</span> range<span class="token punctuation">.</span><span class="token function">random</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
</code></pre><h4 id="when---statement">When - Statement</h4>
<p>Another cool method and/or statement is the <code>when</code>-syntax. I like the way you can, for example, define values with fewer lines of code with it:</p>
<pre><code class="language-kotlin"><span class="token keyword">val</span> knitGauge10Cm <span class="token operator">=</span> <span class="token keyword">when</span><span class="token punctuation">(</span><span class="token function">getYarnWeight</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token string-literal singleline"><span class="token string">"Lace"</span></span> <span class="token operator">-></span> <span class="token string-literal singleline"><span class="token string">"32-34 sts"</span></span>
<span class="token string-literal singleline"><span class="token string">"Fingering"</span></span> <span class="token operator">-></span> <span class="token string-literal singleline"><span class="token string">"28 sts"</span></span>
<span class="token string-literal singleline"><span class="token string">"Sport"</span></span> <span class="token operator">-></span> <span class="token string-literal singleline"><span class="token string">"24-26 sts"</span></span>
<span class="token string-literal singleline"><span class="token string">"DK"</span></span> <span class="token operator">-></span> <span class="token string-literal singleline"><span class="token string">"22 sts"</span></span>
<span class="token string-literal singleline"><span class="token string">"Worsted"</span></span> <span class="token operator">-></span> <span class="token string-literal singleline"><span class="token string">"20 sts"</span></span>
<span class="token string-literal singleline"><span class="token string">"Aran"</span></span> <span class="token operator">-></span> <span class="token string-literal singleline"><span class="token string">"18 sts"</span></span>
<span class="token string-literal singleline"><span class="token string">"Bulky"</span></span> <span class="token operator">-></span> <span class="token string-literal singleline"><span class="token string">"14-15 sts"</span></span>
<span class="token keyword">else</span> <span class="token operator">-></span> <span class="token string-literal singleline"><span class="token string">"Unknown"</span></span>
<span class="token punctuation">}</span>
</code></pre><p>In the example above, <code>when</code> defines a gauge for 10 cm (or 4 inches) for a knit based on yarn weight. When knitting a garment, it is crucial to know how many stitches there are in a certain width of the knit to match the intended size. So if I had an app helping to calculate the gauge for different types of yarns, this example code would be helpful.</p>
<p>If you're interested in reading more about knitting and gauges, check out <a href="https://www.knitpicks.com/learning-center/gauge-guide">Knit Picks' guide about gauge</a>.</p>
<h4 id="repeat---method">Repeat - Method</h4>
<p>The third method I want to mention is the <code>repeat</code>-method. It's so convinient to have a method for repeating something x times. In JavaScript, you naturally can do this - but it takes more lines of code. With Kotlin, you can do that with one function:</p>
<pre><code class="language-kotlin"><span class="token keyword">fun</span> <span class="token function">meow</span><span class="token punctuation">(</span>times<span class="token operator">:</span> Int<span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token function">repeat</span><span class="token punctuation">(</span>times<span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token function">println</span><span class="token punctuation">(</span><span class="token string-literal singleline"><span class="token string">"Meow"</span></span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><h2 id="getting-back-to-android-development">Getting Back to Android Development</h2>
<p>As mentioned at the beginning of the post, I was learning about Android Development years ago. Because of that, it was relatively easy to jump back to the world of Android Studio and other things. And as the first unit concentrates on fundamental things, it felt familiar and easy.</p>
<p>However, I recognized some things I need to practice in the upcoming units. For example, it was difficult to understand the whole <code>ConstraintLayout</code>-concept, and which constraint points to where. I think I will need to think and draw some kind of comparison to CSS layouts to understand the differences. </p>
<p>The next unit will be about layouts, so I'd imagine that will get easier. Also, I'll learn about user input.</p>
<h2 id="wrapping-up">Wrapping Up</h2>
<p>In this blog post, I shared my initial feelings and thoughts from learning about Kotlin and Android development. It's been fun (pun intended), and I'm looking forward to the following units.</p>
<p>In the next blog post, I will cover the second unit, "Layouts." From the Kotlin side, it consists of things like lists, and from the Android side, different layouts and <code>ScrollView</code>.</p>
<p>Have you learned native (Android) development lately? What have your experiences been? Or do you have any tips for a web developer learning about Android development?</p>
<h2 id="links">Links</h2>
<ul>
<li><a href="https://developer.android.com/courses/android-basics-kotlin/course">Android Basics in Kotlin</a></li>
<li><a href="https://www.knitpicks.com/learning-center/gauge-guide">Knit Picks' guide about gauge</a></li>
</ul>
</article>
Results of Quick Testing of Documentation Tools' Accessibility2022-08-11T00:00:00.000Zhttps://eevis.codes/blog/2022-08-11/results-of-quick-testing-of-documentation-tools-accessibility/
<nav id="breadcrumbs" aria-label="breadcrumbs">
<ol>
<li>
<a href="https://eevis.codes/blog/">Blog</a>
<span aria-hidden="true" class="breadcrumb-separator">/<span>
</span></span></li>
<li>Results of Quick Testing of Documentation Tools' Accessibility</li><li>
</li></ol>
</nav>
<article class="blogPostLong">
<h1 id="/2022-08-11/results-of-quick-testing-of-documentation-tools-accessibility">Results of Quick Testing of Documentation Tools' Accessibility</h1>
<div class="reading-time-and-date">
<p>
Reading time: about 9 minutes
</p>
<span class="separator"></span>
<p>Published <time datetime="11th Aug, 2022">11th Aug, 2022</time></p>
</div>
<section class="likes-and-boosts">
<div>
<svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="boosts-title">
<title id="boosts-title">Boosts</title>
<path d="M7.19 4.35047C5.15 6.64047 3.75 9.93047 3.62 10.2405L0 8.69047L4.05 4.64047C4.52 4.17047 5.2 3.96047 5.86 4.09047L7.19 4.35047ZM9.17 15.0005C9.17 15.0005 12.91 13.4505 15.06 11.3005C20.46 5.90047 19.56 1.68047 19.27 0.730473C18.32 0.430473 14.1 -0.459527 8.7 4.94047C6.55 7.09047 5 10.8305 5 10.8305L9.17 15.0005ZM15.65 12.8105C13.36 14.8505 10.07 16.2505 9.76 16.3805L11.31 20.0005L15.36 15.9505C15.83 15.4805 16.04 14.8005 15.91 14.1405L15.65 12.8105ZM7 16.0005C7 16.8305 6.66 17.5805 6.12 18.1205C4.94 19.3005 0 20.0005 0 20.0005C0 20.0005 0.7 15.0605 1.88 13.8805C2.2988 13.4595 2.83325 13.1724 3.4155 13.0558C3.99775 12.9392 4.60153 12.9982 5.15017 13.2253C5.69882 13.4525 6.16758 13.8376 6.49694 14.3317C6.8263 14.8258 7.0014 15.4067 7 16.0005ZM11 7.00047C11 5.90047 11.9 5.00047 13 5.00047C14.1 5.00047 15 5.90047 15 7.00047C15 8.10047 14.1 9.00047 13 9.00047C11.9 9.00047 11 8.10047 11 7.00047Z" fill="#00696D"></path>
</svg>
0
</div>
<div>
<svg role="img" width="20" height="19" viewBox="0 0 20 19" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="likes-title">
<title id="likes-title">Likes</title>
<path d="M10 18.35L8.55 17.03C3.4 12.36 0 9.27 0 5.5C0 2.41 2.42 0 5.5 0C7.24 0 8.91 0.81 10 2.08C11.09 0.81 12.76 0 14.5 0C17.58 0 20 2.41 20 5.5C20 9.27 16.6 12.36 11.45 17.03L10 18.35Z"></path>
</svg>
0
</div>
</section>
<img class="cover-photo" src="https://images.ctfassets.net/mpqufjsy02zr/6E7oE62pKmblXh18uQoMIp/9b3b64b6dc61a4fb08eccf58fafdb45f/Documentation_Tools_A11yon-the-edge-of-burnout__1_.png" alt="Results of Quick Testing of Documentation Tools' Accessibility" />
<div class="blog-tags">
<a href="https://eevis.codes/tags/a11y"><span class="blog-tag turquoise">a11y</span></a> <a href="https://eevis.codes/tags/documentation"><span class="blog-tag purple">documentation</span></a>
</div>
<nav aria-label="Results of Quick Testing of Documentation Tools' Accessibility Table of Contents" class="table-of-contents">
<h2>Table of Contents</h2>
<ol>
<li><a href="https://eevis.codes/blog/2022-08-11/results-of-quick-testing-of-documentation-tools-accessibility/#swagger"> Swagger</a></li><li><a href="https://eevis.codes/blog/2022-08-11/results-of-quick-testing-of-documentation-tools-accessibility/#read-the-docs"> Read the Docs</a></li><li><a href="https://eevis.codes/blog/2022-08-11/results-of-quick-testing-of-documentation-tools-accessibility/#docusaurus"> Docusaurus</a></li><li><a href="https://eevis.codes/blog/2022-08-11/results-of-quick-testing-of-documentation-tools-accessibility/#gitbook"> GitBook</a></li><li><a href="https://eevis.codes/blog/2022-08-11/results-of-quick-testing-of-documentation-tools-accessibility/#final-thoughts"> Final Thoughts</a></li><li><a href="https://eevis.codes/blog/2022-08-11/results-of-quick-testing-of-documentation-tools-accessibility/#links"> Links</a></li>
</ol>
</nav>
<p>When writing my <a href="https://eevis.codes/blog/2022-07-07/how-can-backend-developers-improve-accessibility/">blog post about what backend devs can do for accessibility</a>, I did some quick accessibility tests for different documentation tools. Initially, I thought I'd write about the results in that blog post, but there was just too much to report. So, I ended up writing this blog post.</p>
<p> I chose four tools that I know many projects are using. These tools are:</p>
<ul>
<li>Swagger</li>
<li>Read the Docs</li>
<li>Docusaurus</li>
<li>GitBook</li>
</ul>
<p>First, I ran an automated test with the aXe browser extension and dug deeper into the DOM based on the test results. After that, I tested the site with a keyboard. </p>
<p>In this blog post, I'll share the test results and some thoughts about them. As you can see from the steps described above, the testing was not exhaustive. I just wanted to get an overview of the issues. I did not, for example, test with a screen reader or with narrower viewports (or zooming). </p>
<p>This was a conscious decision, partly because I didn't have time and partly because this blog post would've been super long. And I think this already provides <em>some</em> overview of the state of accessibility of these tools.</p>
<p>Let's get started.</p>
<h2 id="swagger">Swagger</h2>
<p>URL used for testing: <a href="https://petstore3.swagger.io/">https://petstore3.swagger.io/</a></p>
<p>Swagger is a set of tools used for API documentation. The Swagger API project started in 2011. Initially, it was a specification, but it was later named OpenAPI Specification. So it has a long history, and many projects use it. </p>
<p>One of its tools is Swagger UI, a user interface for browsing the API endpoints. That's the one I'm testing for this blog post. </p>
<h3 id="testing-swagger-ui-with-axe">Testing Swagger UI with Axe</h3>
<p>Runnin Axe-browser extension showed 64 issues, two of which are critical, 35 serious, 18 moderate, and 7 minor. Most of the issues (37) were related to color contrast issues. The contrast ratio for the problematic color combinations ranged between 2.03 and 3.75.</p>
<p>Another problem was form input without programmatically associated labels. Some of them were flagged, but when I started digging deeper into the site's code, I noticed that not every instance was flagged. So, there were more problems with form labels than Axe was showing.</p>
<p>The third problem I want to raise is the lack of landmark regions. Not having these regions means that user, who needs these landmark regions for navigating, can't use them. For example, screen reader users and keyboard users who use tools to navigate using landmark regions would need to either listen to a lot more items and/or press a tab many more times to be able to navigate to certain parts of the website. </p>
<h3 id="testing-swagger-ui-with-keyboard">Testing Swagger UI with Keyboard</h3>
<p>The next test was how well I could navigate Swagger UI with a keyboard. From this quick testing, it seems that Swagger UI is working somewhat ok with a keyboard. I could reach every element and interact with them. </p>
<h2 id="read-the-docs">Read the Docs</h2>
<p>URL used for testing: <a href="https://docs.readthedocs.io/en/stable/tutorial/index.html">https://docs.readthedocs.io/en/stable/tutorial/index.html</a></p>
<p>Read the Docs is an open-source documentation platform that was created in 2010. On their website, they say they host over 800 000 open source projects and support over 100 000 users. </p>
<h3 id="testing-read-the-docs-with-axe">Testing Read the Docs with Axe</h3>
<p>Running Axe-browser extension showed 206 issues, 6 critical, 132 serious, and 3 moderate. For Read the Docs, too, the color contrast was the most significant cause of problems. The contrast ratio for the problematic color combinations ranged between 2.36 and 4.19.</p>
<p>Read the Docs uses ARIA-attributes - but not correctly. For example, some headings are annotated as headings with ARIA but without the level of the heading. Here's an example code from the site:</p>
<pre><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>caption<span class="token punctuation">"</span></span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>heading<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>caption-text<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>First steps<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span>
</code></pre><p>Another problem this automated testing revealed is a scrollable area, which is not keyboard accessible. In this case, it's a code-block visualizing warning messages. Lines are longer than the code block, adding horizontal scrolling to the element. And when a user, who uses a keyboard, comes to that particular element and wants to see what's at the end of the line (hidden by scrolling), they can't because that area is not keyboard accessible. </p>
<p>Read the Docs is also missing landmarks, so the same problems as for Swagger UI are present. </p>
<h3 id="testing-read-the-docs-with-keyboard">Testing Read the Docs with Keyboard</h3>
<p>The search field is one of the first focusable items when navigating the page. When the user focuses on it, a modal opens. The "Close"-"button" is actually made of a <code>div</code> without a possibility to focus on it. Fortunately pressing <kbd>Esc</kbd> closes the modal.</p>
<p>When the modal is visible, the focus is set to the modal's first focusable element - the search bar. However, the focus is not trapped inside the modal, it's only moved there, and the modal is the last item on the DOM. The user can still reach the underlying links and other focusable elements if they navigate backward (which is definitely not optimal).</p>
<p>After closing the search modal, the focus is not managed. At least on my tests on Mac, Chrome, and Firefox, if I close the modal and then press <kbd>Tab</kbd>, the focus ends up in the first tab of my browser. </p>
<p>Combined with the on-focus behavior of the modal, this creates a sort of focus trap - the user can't reach the rest of the page by tabbing forward. Yes, they can do that by navigating backward with <kbd>Shift</kbd> and <kbd>Tab</kbd>, but that's problematic. And if the user can survive without the information on the page, they probably leave instead of trying to find all the ways they could navigate the site.</p>
<p>Another thing I wanted to mention from the keyboard navigation point of view is that there's no "Skip to content"-link. If the user somehow gets past the search modal, they need to tab through all of the links in the left side panel to reach the main content. On the example page, that means a lot of links to tab through. </p>
<h2 id="docusaurus">Docusaurus</h2>
<p>URLs used for testing:</p>
<ul>
<li><a href="https://docusaurus.io/docs">https://docusaurus.io/docs</a> and <a href="https://docusaurus.io/docs/styling-layout">https://docusaurus.io/docs/styling-layout</a> for automated testing</li>
<li><a href="https://docusaurus.io/docs/styling-layout">https://docusaurus.io/docs/styling-layout</a> for other tests</li>
</ul>
<p>Docusaurus is a static site generator created with React. On the Introduction page, they say that </p>
<blockquote>
<p> It provides out-of-the-box documentation features but can be used to create any kind of site (personal website, product, blog, marketing landing pages, etc.).</p>
</blockquote>
<p>The team behind the Docusaurus is from Meta, as it's one of its Open Source projects. </p>
<p>I'm testing with Docusaurus version 2. At the time of testing, it was still in beta. However, now it's published. I chose two pages to test so that I'd get a bit more components to experiment with. </p>
<h3 id="testing-docusaurus-with-axe">Testing Docusaurus with Axe</h3>
<p>Testing Docusaurus with Axe revealed 51 issues on the "Styling and Layout" page and 15 on the "Introduction"-page. From the first, 8 were "Needs review", 1 critical, 41 serious, and 1 moderate. On the "Introduction" page, 14 needed review, and 1 was moderate.</p>
<p>Most of the issues are about color contrast - on the "Styling and Layout"-page, 46 of the issues were about that. And some of the problems were because of examples of failing color contrast, so that's expected. Others were mostly from the code snippets' styles.</p>
<p>Other problems consisted of missing labels on a color input and non-unique landmarks. </p>
<h3 id="testing-docusaurus-with-keyboard">Testing Docusaurus with Keyboard</h3>
<p>Most of the problems I encountered when testing Docusaurus with the keyboard were related to focus management. For example, when I opened the search modal and left it without typing anything on the search field, the focus was not managed and ended up at the beginning of the page.</p>
<p>Another possible problem (although not a WCAG violation) is that Docusaurus uses the browser's default focus styles. In some cases, the focus is hard to see, especially if the theme of the documents is close to the default focus indicator's color.</p>
<p>One thing I noticed is the menu's dropdown-pattern Docusaurus uses. You can navigate the items on the dropdowns with <kbd>Tab</kbd>, but that's not the correct pattern for dropdowns in the menubar. Per <a href="https://www.w3.org/WAI/ARIA/apg/patterns/menu/">ARIA Authoring Practices</a>, the correct pattern would be that navigation happens with up and down arrows, and <kbd>Tab</kbd> gets you to the next widget.</p>
<p>I also found good things: Docusaurus has a "Skip to main content" link. It works - this is not always the case with skip-to-content links. Another nice thing is that the color picker in the "Styling and layout"-page works with a keyboard.</p>
<h2 id="gitbook">GitBook</h2>
<p>URL used for testing: <a href="https://docs.gitbook.com/editing-content/rich-content/with-command-palette">https://docs.gitbook.com/editing-content/rich-content/with-command-palette</a></p>
<p>GitBook is a documentation platform with lots of different features. It started as a simple open-source tool and grew into what it is today. It's free for open-source projects and priced for everyone else.</p>
<h3 id="testing-gitbook-with-axe">Testing GitBook with Axe</h3>
<p>When I ran aXe on GitBook, there were 237 issues, from which 27 needed review, 2 were critical, 65 serious, and 143 moderate. </p>
<p>There were many color contrast issues (61), and the non-passing elements had contrast ratios between 2,53 and 4.48. Also, there were problems with HTML semantics; <code>aria-label</code>s on <code>div</code>s, lists with incorrect markup, and <code>lang</code>-attribute missing. </p>
<p>It also had missing landmarks and some missing alt-texts. And it had zooming disabled; this means that if someone needs to zoom in to see better, they can't. It's a terrible practice, making the site unusable for some users. </p>
<h3 id="testing-gitbook-with-keyboard">Testing GitBook with Keyboard</h3>
<p>Continuing with the theme "Unusable for some users", GitBook doesn't have a focus indicator for most elements, so it's frustrating to try navigating with a keyboard. Because of that, I couldn't test the site much with a keyboard.</p>
<h2 id="final-thoughts">Final Thoughts</h2>
<p>In this blog post, I've shared results from quick tests I've made on four documentation tools: Swagger UI, Read the Docs, Docusaurus, and GitBook. Each has accessibility problems, but I would definitely pick Docusaurus for documentation from these four. </p>
<p>After testing all the tools, I researched how they have reacted to accessibility. I wanted to know if they have accessibility statements on their pages, how they track accessibility issues, etc.</p>
<p>Research on Swagger led me to Github. <a href="https://github.com/swagger-api/swagger-ui/issues?q=is%3Aopen+is%3Aissue+label%3A%22cat%3A+a11y%22">Swagger tracks the accessibility issues with a <code>cat: a11y</code>-tag</a>, but there seems to have been no activity this year. Search with "accessibility" on Swagger's site did not find anything, so I'm assuming they don't have any statements. </p>
<p>Read the Docs has no policies or statements on accessibility. They have some Github issues when searching for accessibility in the theme's repository. Read the Docs has a theming system, so in theory, the user can fix some of the problems found with the search. And with "some", I mean the ones related to styles (so, color, for example). If I've understood correctly, these themes are about styles, not the underlying HTML.</p>
<p>In Docusaurus' introduction, they say that Docusaurus has "attention to accessibility, making your site equally accessible to all users." They also track accessibility issues with the <code>domain: a11y</code>-tag in Github. However, I couldn't find any separate accessibility statement for Docusaurus.</p>
<p>Last, Gitbook mentions accessibility only in "Space customisation"'s "Primary color" section, where there is a mention about accessibility being important when selecting colors. When searching for accessibility and Gitbook, I found a couple of pages using Gitbook and writing about accessibility. That's a bit ironic - as the Gitbook itself is not that accessible.</p>
<p>What's your favorite documentation tool? Have you thought about or tested its accessibility? </p>
<h2 id="links">Links</h2>
<ul>
<li><a href="https://eevis.codes/blog/2022-07-07/how-can-backend-developers-improve-accessibility/">blog post about what backend devs can do for accessibility</a></li>
<li><a href="https://petstore3.swagger.io/">https://petstore3.swagger.io/</a></li>
<li><a href="https://docs.readthedocs.io/en/stable/tutorial/index.html">https://docs.readthedocs.io/en/stable/tutorial/index.html</a></li>
<li><a href="https://docusaurus.io/docs">https://docusaurus.io/docs</a></li>
<li><a href="https://docusaurus.io/docs/styling-layout">https://docusaurus.io/docs/styling-layout</a></li>
<li><a href="https://www.w3.org/WAI/ARIA/apg/patterns/menu/">ARIA Authoring Practices</a></li>
<li><a href="https://docs.gitbook.com/editing-content/rich-content/with-command-palette">https://docs.gitbook.com/editing-content/rich-content/with-command-palette</a></li>
<li><a href="https://github.com/swagger-api/swagger-ui/issues?q=is%3Aopen+is%3Aissue+label%3A%22cat%3A+a11y%22">Swagger tracks the accessibility issues with a <code>cat: a11y</code>-tag</a></li>
</ul>
</article>
Notes of a Web Developer Learning Kotlin and Android Development - part 22022-08-18T00:00:00.000Zhttps://eevis.codes/blog/2022-08-18/notes-of-a-web-developer-learning-kotlin-and-android-development-part-2/
<nav id="breadcrumbs" aria-label="breadcrumbs">
<ol>
<li>
<a href="https://eevis.codes/blog/">Blog</a>
<span aria-hidden="true" class="breadcrumb-separator">/<span>
</span></span></li>
<li>Notes of a Web Developer Learning Kotlin and Android Development - part 2</li><li>
</li></ol>
</nav>
<article class="blogPostLong">
<h1 id="/2022-08-18/notes-of-a-web-developer-learning-kotlin-and-android-development-part-2">Notes of a Web Developer Learning Kotlin and Android Development - part 2</h1>
<div class="reading-time-and-date">
<p>
Reading time: about 5 minutes
</p>
<span class="separator"></span>
<p>Published <time datetime="18th Aug, 2022">18th Aug, 2022</time></p>
</div>
<section class="likes-and-boosts">
<div>
<svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="boosts-title">
<title id="boosts-title">Boosts</title>
<path d="M7.19 4.35047C5.15 6.64047 3.75 9.93047 3.62 10.2405L0 8.69047L4.05 4.64047C4.52 4.17047 5.2 3.96047 5.86 4.09047L7.19 4.35047ZM9.17 15.0005C9.17 15.0005 12.91 13.4505 15.06 11.3005C20.46 5.90047 19.56 1.68047 19.27 0.730473C18.32 0.430473 14.1 -0.459527 8.7 4.94047C6.55 7.09047 5 10.8305 5 10.8305L9.17 15.0005ZM15.65 12.8105C13.36 14.8505 10.07 16.2505 9.76 16.3805L11.31 20.0005L15.36 15.9505C15.83 15.4805 16.04 14.8005 15.91 14.1405L15.65 12.8105ZM7 16.0005C7 16.8305 6.66 17.5805 6.12 18.1205C4.94 19.3005 0 20.0005 0 20.0005C0 20.0005 0.7 15.0605 1.88 13.8805C2.2988 13.4595 2.83325 13.1724 3.4155 13.0558C3.99775 12.9392 4.60153 12.9982 5.15017 13.2253C5.69882 13.4525 6.16758 13.8376 6.49694 14.3317C6.8263 14.8258 7.0014 15.4067 7 16.0005ZM11 7.00047C11 5.90047 11.9 5.00047 13 5.00047C14.1 5.00047 15 5.90047 15 7.00047C15 8.10047 14.1 9.00047 13 9.00047C11.9 9.00047 11 8.10047 11 7.00047Z" fill="#00696D"></path>
</svg>
0
</div>
<div>
<svg role="img" width="20" height="19" viewBox="0 0 20 19" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="likes-title">
<title id="likes-title">Likes</title>
<path d="M10 18.35L8.55 17.03C3.4 12.36 0 9.27 0 5.5C0 2.41 2.42 0 5.5 0C7.24 0 8.91 0.81 10 2.08C11.09 0.81 12.76 0 14.5 0C17.58 0 20 2.41 20 5.5C20 9.27 16.6 12.36 11.45 17.03L10 18.35Z"></path>
</svg>
0
</div>
</section>
<img class="cover-photo" src="https://images.ctfassets.net/mpqufjsy02zr/7j78KHLnSoeXQlZx0gQDwb/c343b3d98b6446d45a30fab65200c972/Kotlin-Android-part2on-the-edge-of-burnout.png" alt="Notes of a web developer learning Kotlin and Android development - part 2." />
<div class="blog-tags">
<a href="https://eevis.codes/tags/backend"><span class="blog-tag turquoise">backend</span></a> <a href="https://eevis.codes/tags/Kotlin"><span class="blog-tag turquoise">Kotlin</span></a>
</div>
<nav aria-label="Notes of a Web Developer Learning Kotlin and Android Development - part 2 Table of Contents" class="table-of-contents">
<h2>Table of Contents</h2>
<ol>
<li><a href="https://eevis.codes/blog/2022-08-18/notes-of-a-web-developer-learning-kotlin-and-android-development-part-2/#kotlin-lists-and-other-things"> Kotlin: Lists and Other Things</a></li><li><a href="https://eevis.codes/blog/2022-08-18/notes-of-a-web-developer-learning-kotlin-and-android-development-part-2/#android-lets-talk-about-adapters"> Android: Let's Talk About Adapters</a></li><li><a href="https://eevis.codes/blog/2022-08-18/notes-of-a-web-developer-learning-kotlin-and-android-development-part-2/#wrapping-up"> Wrapping Up</a></li><li><a href="https://eevis.codes/blog/2022-08-18/notes-of-a-web-developer-learning-kotlin-and-android-development-part-2/#links"> Links</a></li>
</ol>
</nav>
<p><em>This blog post is the second in a series of posts about me working through the <a href="https://developer.android.com/courses/android-basics-kotlin/course">Android Basics on Kotlin-course</a>. You can find the first one here: <a href="https://eevis.codes/blog/2022-08-02/notes-of-a-web-developer-learning-kotlin-and-android-development-part-1/">Notes of a Web Developer Learning Kotlin and Android Development - part 1</a></em></p>
<p>The second unit of the course, "Android Basics in Kotlin," is about Layouts. It also goes through some Kotlin-concepts, such as lists and <code>with</code>-syntax. From the Android side, the most significant theme is layouts, particularly adapters.</p>
<h2 id="kotlin-lists-and-other-things">Kotlin: Lists and Other Things</h2>
<h3 id="lists">Lists</h3>
<p>There are different list types in Kotlin. Lists can be either mutable or immutable - you need to decide which one when defining the list. That's different from JavaScript: Even though you define them as constant in JS, you can still add and remove things. In Kotlin, you need to have a mutable list to do that. </p>
<p>Lists are defined with the following syntaxes:</p>
<pre><code class="language-kotlin"><span class="token keyword">val</span> immutableList <span class="token operator">=</span> <span class="token function">listOf</span><span class="token punctuation">(</span><span class="token string-literal singleline"><span class="token string">"list"</span></span><span class="token punctuation">,</span> <span class="token string-literal singleline"><span class="token string">"of"</span></span><span class="token punctuation">,</span> <span class="token string-literal singleline"><span class="token string">"words"</span></span><span class="token punctuation">)</span>
<span class="token keyword">val</span> mutableList <span class="token operator">=</span> <span class="token function">mutableListOf</span><span class="token punctuation">(</span><span class="token string-literal singleline"><span class="token string">"other"</span></span><span class="token punctuation">,</span> <span class="token string-literal singleline"><span class="token string">"words"</span></span><span class="token punctuation">)</span>
</code></pre><p>Printing these would result in</p>
<pre><code class="language-shell"><span class="token punctuation">[</span>list, of, words<span class="token punctuation">]</span>
</code></pre><p>and</p>
<pre><code class="language-shell"><span class="token punctuation">[</span>other, words<span class="token punctuation">]</span>
</code></pre><p>You can get the first item from a list with method <code>first()</code> and the last item with method <code>last()</code>. Sorting a list happens with the <code>sorted()</code>-method, and you can reverse a list with <code>reversed()</code>.</p>
<p>Mutable lists have a set of additional methods to mutate the list; here are some examples:</p>
<dl>
<dt><code>add()</code></dt>
<dd>Add an item to the list.</dd>
<dt><code>addAll()</code></dt>
<dd>Add a collection to the list. More about other collection types in the next part.</dd>
<dt><code>remove()</code></dt>
<dd>Remove an item from the list.</dd>
<dt><code>clear()</code></dt>
<dd>Clear the list.</dd>
<dt><code>isEmpty()</code></dt>
<dd>Check if the list is empty.</dd>
</dl>
<p>Again, here's plenty more than what you'll have with JavaScript. I mean, you can do all of these with JS, but you need to write more code for that, and it's not as verbose. Let's take a look at checking if a list is empty. With Kotlin, it would be:</p>
<pre><code class="language-kotlin"><span class="token comment">// Kotlin</span>
<span class="token keyword">val</span> list <span class="token operator">=</span> <span class="token function">listOf</span><span class="token punctuation">(</span><span class="token operator">..</span><span class="token punctuation">.</span><span class="token punctuation">)</span>
<span class="token keyword">val</span> isListEmpty <span class="token operator">=</span> list<span class="token punctuation">.</span><span class="token function">isEmpty</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
</code></pre><p>And with JavaScript:</p>
<pre><code class="language-javascript"><span class="token comment">// JavaScript</span>
<span class="token keyword">const</span> list <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token spread operator">...</span><span class="token punctuation">]</span>
<span class="token keyword">const</span> isListEmpty <span class="token operator">=</span> list<span class="token punctuation">.</span><span class="token property-access">length</span> <span class="token operator">===</span> <span class="token number">0</span>
<span class="token comment">// or</span>
<span class="token keyword">const</span> isListEmptyShorter <span class="token operator">=</span> <span class="token operator">!</span>list<span class="token punctuation">.</span><span class="token property-access">length</span>
</code></pre><p>A final thing about the lists in this post is looping through them. The material mentions two methods - <code>while</code> and <code>for</code>-loops. </p>
<p><code>while</code>-loops go on, as long its condition is satisfied. It first checks the condition and executes the body if it's true.</p>
<pre><code class="language-kotlin"><span class="token keyword">while</span> <span class="token punctuation">(</span>x <span class="token operator"><</span> <span class="token number">10</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token comment">// Do something</span>
<span class="token operator">++</span>x
<span class="token punctuation">}</span>
</code></pre><p>For-loops iterates through the collection provided. So, for example, looping through the range of numbers could look like this:</p>
<pre><code class="language-kotlin"><span class="token keyword">for</span> <span class="token punctuation">(</span>i <span class="token keyword">in</span> <span class="token number">1</span><span class="token operator">..</span><span class="token number">6</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token comment">// Do something with the current number</span>
<span class="token punctuation">}</span>
</code></pre><h3 id="with"><code>with</code></h3>
<p><code>with</code>-syntax is nice when working with, for example, a specific class instance, and there's a need to access more than one of its properties and functions. </p>
<p>So, for example:</p>
<pre><code class="language-kotlin"><span class="token function">with</span><span class="token punctuation">(</span>cat<span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token function">println</span><span class="token punctuation">(</span><span class="token string-literal singleline"><span class="token string">"Cat's name is </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span><span class="token expression">name</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">"</span></span><span class="token punctuation">)</span>
<span class="token function">println</span><span class="token punctuation">(</span><span class="token string-literal singleline"><span class="token string">"It often sleeps in </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span><span class="token expression">favoritePlace</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">"</span></span><span class="token punctuation">)</span>
<span class="token function">meow</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
</code></pre><p>I could access these properties with <code>cat.name</code> or <code>cat.meow()</code>, but I need to write less code this way.</p>
<h2 id="android-lets-talk-about-adapters">Android: Let's Talk About Adapters</h2>
<p>Adapters are the most significant learning in this unit - or refresher, as I recall some distant things about them. They work as, you guessed, adapters between views and underlying data. </p>
<p>The adapter is a design pattern. It adapts the data and transforms it into something the client can use. In the course, the example is about <code>RecyclerView</code>, and how the data is adapted to it.</p>
<p>When introducing the adapters, the app being built is a list of affirmations. <code>RecyclerView</code> is suitable for this project as it's a good component for creating dynamic lists efficiently. </p>
<p>The course explains how an adapter works with <code>RecyclerView</code> in the app with the following words:</p>
<blockquote>
<p>When you run the app, RecyclerView uses the adapter to figure out how to display your data on screen. RecyclerView asks the adapter to create a new list item view for the first data item in your list. Once it has the view, it asks the adapter to provide the data to draw the item. This process repeats until the RecyclerView doesn't need any more views to fill the screen. If only 3 list item views fit on the screen at once, the RecyclerView only asks the adapter to prepare those 3 list item views (instead of all 10 list item views).</p>
</blockquote>
<p>In the app, the adapter has multiple parts. First, a layout is created for an individual list item. The next step is to create an <code>ItemAdapter</code>-class, which takes in a list of affirmations, and context-object instance. </p>
<p>After that, an <code>ItemViewHolder</code> is created inside the <code>ItemAdapter</code>. It represents a single list item view in the <code>RecyclerView</code> and can be reused. That view holder is the one that handles updating and showing data. </p>
<p>The adapter needs to implement three methods: <code>onCreateViewHolder</code>, <code>getItemCount</code> and <code>onBindViewHolder</code>. </p>
<p>The layout manager calls <code>onCreateViewHolder</code> to create new view holders for the <code>RecyclerView</code>, if there are no existing view holders that it could reuse. </p>
<p><code>getItemCount</code> is for, as the name suggests, getting the item count for the list data. The layout manager calls <code>onBindViewHolder</code> to replace the contents of a list item view.</p>
<p>There's one more step: Once the adapter is created, <code>RecyclerView</code> needs to know about it. In the course project, <code>MainActivity</code> is the place to do that. To be more specific, in the <code>onCreate</code>-method of that activity.</p>
<p>I've had a hard time wrapping my head around the pattern. However, I think it's partly because it's a new, complex thing, and to be honest, it's been a while since the last last time I did object-oriented programming.</p>
<p>So I need to keep practicing - this concept among all the others. Luckily, from what I've understood, this is something I'll need many times when learning more about Android development.</p>
<h2 id="wrapping-up">Wrapping Up</h2>
<p>There were many things to learn in unit 2 of the Android Basics in Kotlin-course. I know I will return to these topics in the following units - and definitely when I start creating something of my own. </p>
<p>The next unit is about navigation, and that's an interesting one. I've already started it, and there's so much to know! I'm so excited about these new concepts I'm learning. As there is so much material and new things in the Navigation unit, I might split it into two posts. We'll see.</p>
<p>Have you been learning Kotlin or Android development? Have any tips or thoughts?</p>
<h2 id="links">Links</h2>
<ul>
<li><a href="https://developer.android.com/courses/android-basics-kotlin/course">Android Basics on Kotlin-course</a></li>
<li><a href="https://eevis.codes/blog/2022-08-02/notes-of-a-web-developer-learning-kotlin-and-android-development-part-1/">Notes of a Web Developer Learning Kotlin and Android Development - part 1</a></li>
</ul>
</article>
Learnings From Creating a Guest Book App2022-08-24T00:00:00.000Zhttps://eevis.codes/blog/2022-08-24/learnings-from-creating-a-guest-book-app/
<nav id="breadcrumbs" aria-label="breadcrumbs">
<ol>
<li>
<a href="https://eevis.codes/blog/">Blog</a>
<span aria-hidden="true" class="breadcrumb-separator">/<span>
</span></span></li>
<li>Learnings From Creating a Guest Book App</li><li>
</li></ol>
</nav>
<article class="blogPostLong">
<h1 id="/2022-08-24/learnings-from-creating-a-guest-book-app">Learnings From Creating a Guest Book App</h1>
<div class="reading-time-and-date">
<p>
Reading time: about 5 minutes
</p>
<span class="separator"></span>
<p>Published <time datetime="24th Aug, 2022">24th Aug, 2022</time></p>
</div>
<section class="likes-and-boosts">
<div>
<svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="boosts-title">
<title id="boosts-title">Boosts</title>
<path d="M7.19 4.35047C5.15 6.64047 3.75 9.93047 3.62 10.2405L0 8.69047L4.05 4.64047C4.52 4.17047 5.2 3.96047 5.86 4.09047L7.19 4.35047ZM9.17 15.0005C9.17 15.0005 12.91 13.4505 15.06 11.3005C20.46 5.90047 19.56 1.68047 19.27 0.730473C18.32 0.430473 14.1 -0.459527 8.7 4.94047C6.55 7.09047 5 10.8305 5 10.8305L9.17 15.0005ZM15.65 12.8105C13.36 14.8505 10.07 16.2505 9.76 16.3805L11.31 20.0005L15.36 15.9505C15.83 15.4805 16.04 14.8005 15.91 14.1405L15.65 12.8105ZM7 16.0005C7 16.8305 6.66 17.5805 6.12 18.1205C4.94 19.3005 0 20.0005 0 20.0005C0 20.0005 0.7 15.0605 1.88 13.8805C2.2988 13.4595 2.83325 13.1724 3.4155 13.0558C3.99775 12.9392 4.60153 12.9982 5.15017 13.2253C5.69882 13.4525 6.16758 13.8376 6.49694 14.3317C6.8263 14.8258 7.0014 15.4067 7 16.0005ZM11 7.00047C11 5.90047 11.9 5.00047 13 5.00047C14.1 5.00047 15 5.90047 15 7.00047C15 8.10047 14.1 9.00047 13 9.00047C11.9 9.00047 11 8.10047 11 7.00047Z" fill="#00696D"></path>
</svg>
0
</div>
<div>
<svg role="img" width="20" height="19" viewBox="0 0 20 19" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="likes-title">
<title id="likes-title">Likes</title>
<path d="M10 18.35L8.55 17.03C3.4 12.36 0 9.27 0 5.5C0 2.41 2.42 0 5.5 0C7.24 0 8.91 0.81 10 2.08C11.09 0.81 12.76 0 14.5 0C17.58 0 20 2.41 20 5.5C20 9.27 16.6 12.36 11.45 17.03L10 18.35Z"></path>
</svg>
0
</div>
</section>
<img class="cover-photo" src="https://images.ctfassets.net/mpqufjsy02zr/1JQwvh0lyHuXULSqSF7Q6A/b602b7d1bc857592fddcc8783be2bdf5/Kotlin-Android-part2on-the-edge-of-burnout__1_.png" alt="Learnings From Creating a Guest Book App" />
<div class="blog-tags">
<a href="https://eevis.codes/tags/NextJS"><span class="blog-tag orange">NextJS</span></a> <a href="https://eevis.codes/tags/GraphQL"><span class="blog-tag purple">GraphQL</span></a>
</div>
<nav aria-label="Learnings From Creating a Guest Book App Table of Contents" class="table-of-contents">
<h2>Table of Contents</h2>
<ol>
<li><a href="https://eevis.codes/blog/2022-08-24/learnings-from-creating-a-guest-book-app/#the-guest-book-app"> The Guest Book App</a></li><li><a href="https://eevis.codes/blog/2022-08-24/learnings-from-creating-a-guest-book-app/#theres-always-a-typo-somewhere"> There's Always a Typo Somewhere</a></li><li><a href="https://eevis.codes/blog/2022-08-24/learnings-from-creating-a-guest-book-app/#netlify-nextjs-images-and-orientation"> Netlify, NextJS Images, and Orientation</a></li><li><a href="https://eevis.codes/blog/2022-08-24/learnings-from-creating-a-guest-book-app/#simple-things-go-a-long-way"> Simple Things Go a Long Way</a></li><li><a href="https://eevis.codes/blog/2022-08-24/learnings-from-creating-a-guest-book-app/#stepping-out-from-the-tech-bubble-does-you-good"> Stepping Out From the Tech Bubble Does You Good</a></li><li><a href="https://eevis.codes/blog/2022-08-24/learnings-from-creating-a-guest-book-app/#wrapping-up"> Wrapping Up</a></li><li><a href="https://eevis.codes/blog/2022-08-24/learnings-from-creating-a-guest-book-app/#links"> Links</a></li>
</ol>
</nav>
<p>So it seems my sister is currently my biggest muse for side projects. She was the reason I created <a href="https://neule.art/en/">Neule.art</a>, from which you can read more in <a href="https://eevis.codes/blog/2022-06-30/how-i-created-neule-art/">the blog post "How I created Neule.art"</a>.</p>
<p>A while back, she asked me if I could create a guest book app for a party they had with their friends. The idea was simple - there should be a possibility to add a photo, some text, and the name(s) of the sender(s). They tried to search for a readymade app, but every one of them had some problems.</p>
<p>I also know, from experience, that these kinds of apps can be... how to put this... Not so good usability-wise. For example, no one wants to download an app for one night to be able to take one or two pictures. No one wants to create an account for that one night (and then forget that they have it). </p>
<p>So I wanted to try out if I could build a simple enough, non-account-needing web app. In this blog post, I won't share the code for that app. It was a bit hacky, as the app needed to work only for that one night. Aaand I was a bit in a hurry. However, I will share some learnings from creating that app. Let's first have a look at what the app was like.</p>
<h2 id="the-guest-book-app">The Guest Book App</h2>
<p>The guest book app consisted of two pages: Login, which had this one input field for writing the password, and a page for guest book entries. In addition, there was a modal for adding a new entry. That had a form with the possibility of taking a photo (a file-type input field with <code>accept="image/*"</code> to open the camera) and adding names and a message.</p>
<p>As for the tech stack, I used NextJS with TypeScript, GraphQL, and GraphCMS (at the time, they changed their name just after I finished the project to Hygraph) for CMS. As for the photos, I stored them in an AWS S3 bucket and their URL to the CMS.</p>
<p>I've worked with all the other technologies before, but AWS was something new. However, I was pretty sure that because it's a fairly common use case to store things in AWS S3 buckets, and as React is so popular, there must be an easy solution for uploading files to S3 buckets from React apps. Turns out there is - but it wasn't that easy to upload the photos. That leads us to the first lesson learned. </p>
<h2 id="theres-always-a-typo-somewhere">There's Always a Typo Somewhere</h2>
<p>As mentioned, this was my first time working with AWS. But I've heard the stories (mainly about someone forgetting to turn off something and getting an astronomical bill when they realize it the following day) - and I was prepared for problems. </p>
<p>I followed some tutorials on setting up an AWS S3 bucket, and to my surprise, that was easy. The problems started when I tried to upload the photo to the bucket. I tried a couple of packages and had CORS issues I couldn't solve. </p>
<p>I didn't understand why, and I was already ready to quit software development after spending a couple of days banging my head on the wall because of this issue. But I finally solved it.</p>
<p>The cause? Typo in the bucket name in the code.</p>
<p>The following tweet is highly related to this situation.</p>
<p><a href="https://twitter.com/EevisPanula/status/1545704858472292352">https://twitter.com/EevisPanula/status/1545704858472292352</a></p>
<h2 id="netlify-nextjs-images-and-orientation">Netlify, NextJS Images, and Orientation</h2>
<p>Another issue I had (way more minor than the CORS issues) was with the NextJS Image component and Netlify. I had planned to use Netlify for hosting, and I was almost finished. I deployed the app and sent it for testing. </p>
<p>It turned out that Netlify removed the EXIF data from the images for some reason. Some of the photos were weirdly rotated. I found some discussions where (if I remember correctly) the team confirmed that this is what can happen. While writing this blog post, I searched for the issues/forum posts/anything I've read, but I couldn't find anything. So I can't back this up.</p>
<p>I would have loved to solve this problem in some elegant way. However, at that point, I didn't have too much time to finish the app. So I decided to use Vercel for hosting - NextJS images worked well with it as it's the company behind NextJS. </p>
<h2 id="simple-things-go-a-long-way">Simple Things Go a Long Way</h2>
<p>Sometimes, we need to remind ourselves that simple things go a long way. When we build apps and sites, it doesn't always have to be something super complicated and have a multitude of features. </p>
<p>As I mentioned initially, this app aimed to be as simple as possible. It was an answer to my (and others') frustrations with the available guest book apps that either required app installation or an account (or both). And it delivered what it was supposed to: People could leave guest book entries for the hosts. </p>
<p>I'm not saying this app was distribution-ready, but it would have been a good starting point had I had time to make it more configurable. Or to open source it, because right now, the codebase looks like I hacked it together in a couple of days. That's precisely what I did. </p>
<p>I try to remind my mentees and anyone seeking my advice that it's better to have a simple app completed than a bigger one in progress when applying for jobs. The simple app can always have improvements and next steps, but I try to stress that it's good to have the MVP finished. </p>
<h2 id="stepping-out-from-the-tech-bubble-does-you-good">Stepping Out From the Tech Bubble Does You Good</h2>
<p>The fourth thing I want to talk about is that if you usually spend time with developers (like I do), try sometimes showing your projects to someone outside that bubble. It can really boost your confidence.</p>
<p>When I went to the party and spoke with one of the hosts, it helped me to remember that what I can do is a lot. If I'm showing my projects to anyone who knows about coding, I think a lot about what could be better and the improvements. Case in point: I don't want to share the code with you, my reader. </p>
<p>Another person who I can rely upon to remind me about these points is my sister. I love her enthusiasm when I show her something I've coded. It's been like that ever since I started to learn to code, and I appreciate her for that </p>
<h2 id="wrapping-up">Wrapping Up</h2>
<p>It was fun to create a project for someone's use. Even though there were some problems to solve, I'm proud of myself. I'm proud that I made this app, and it was usable. I'm also proud that I solved those problems. </p>
<p>Do you have any apps/sites in progress? Or have you finished something lately? </p>
<h2 id="links">Links</h2>
<ul>
<li><a href="https://neule.art/en/">Neule.art</a></li>
<li><a href="https://eevis.codes/blog/2022-06-30/how-i-created-neule-art/">The blog post "How I created Neule.art"</a></li>
<li><a href="https://twitter.com/EevisPanula/status/1545704858472292352">Link to the tweet</a></li>
</ul>
</article>
Hey Tech Recruiter, Here Are Some Tips from a Developer2022-11-06T00:00:00.000Zhttps://eevis.codes/blog/2022-11-06/hey-tech-recruiter-here-are-some-tips-from-a-developer/
<nav id="breadcrumbs" aria-label="breadcrumbs">
<ol>
<li>
<a href="https://eevis.codes/blog/">Blog</a>
<span aria-hidden="true" class="breadcrumb-separator">/<span>
</span></span></li>
<li>Hey Tech Recruiter, Here Are Some Tips from a Developer</li><li>
</li></ol>
</nav>
<article class="blogPostLong">
<h1 id="/2022-11-06/hey-tech-recruiter-here-are-some-tips-from-a-developer">Hey Tech Recruiter, Here Are Some Tips from a Developer</h1>
<div class="reading-time-and-date">
<p>
Reading time: about 9 minutes
</p>
<span class="separator"></span>
<p>Published <time datetime="6th Nov, 2022">6th Nov, 2022</time></p>
</div>
<section class="likes-and-boosts">
<div>
<svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="boosts-title">
<title id="boosts-title">Boosts</title>
<path d="M7.19 4.35047C5.15 6.64047 3.75 9.93047 3.62 10.2405L0 8.69047L4.05 4.64047C4.52 4.17047 5.2 3.96047 5.86 4.09047L7.19 4.35047ZM9.17 15.0005C9.17 15.0005 12.91 13.4505 15.06 11.3005C20.46 5.90047 19.56 1.68047 19.27 0.730473C18.32 0.430473 14.1 -0.459527 8.7 4.94047C6.55 7.09047 5 10.8305 5 10.8305L9.17 15.0005ZM15.65 12.8105C13.36 14.8505 10.07 16.2505 9.76 16.3805L11.31 20.0005L15.36 15.9505C15.83 15.4805 16.04 14.8005 15.91 14.1405L15.65 12.8105ZM7 16.0005C7 16.8305 6.66 17.5805 6.12 18.1205C4.94 19.3005 0 20.0005 0 20.0005C0 20.0005 0.7 15.0605 1.88 13.8805C2.2988 13.4595 2.83325 13.1724 3.4155 13.0558C3.99775 12.9392 4.60153 12.9982 5.15017 13.2253C5.69882 13.4525 6.16758 13.8376 6.49694 14.3317C6.8263 14.8258 7.0014 15.4067 7 16.0005ZM11 7.00047C11 5.90047 11.9 5.00047 13 5.00047C14.1 5.00047 15 5.90047 15 7.00047C15 8.10047 14.1 9.00047 13 9.00047C11.9 9.00047 11 8.10047 11 7.00047Z" fill="#00696D"></path>
</svg>
0
</div>
<div>
<svg role="img" width="20" height="19" viewBox="0 0 20 19" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="likes-title">
<title id="likes-title">Likes</title>
<path d="M10 18.35L8.55 17.03C3.4 12.36 0 9.27 0 5.5C0 2.41 2.42 0 5.5 0C7.24 0 8.91 0.81 10 2.08C11.09 0.81 12.76 0 14.5 0C17.58 0 20 2.41 20 5.5C20 9.27 16.6 12.36 11.45 17.03L10 18.35Z"></path>
</svg>
0
</div>
</section>
<img class="cover-photo" src="https://images.ctfassets.net/mpqufjsy02zr/7vTlp9zRHEBA0bGrZDHkG7/19f47697fc50ac125486a5442a31f3c7/tech-recruiteron-the-edge-of-burnout.png" alt="Hey Tech Recruiter, Here Are Some Tips from a Developer" />
<div class="blog-tags">
<a href="https://eevis.codes/tags/career"><span class="blog-tag purple">career</span></a>
</div>
<nav aria-label="Hey Tech Recruiter, Here Are Some Tips from a Developer Table of Contents" class="table-of-contents">
<h2>Table of Contents</h2>
<ol>
<li><a href="https://eevis.codes/blog/2022-11-06/hey-tech-recruiter-here-are-some-tips-from-a-developer/#dont-call-me"> Don't Call Me</a></li><li><a href="https://eevis.codes/blog/2022-11-06/hey-tech-recruiter-here-are-some-tips-from-a-developer/#know-the-basics-of-tech"> Know The Basics of Tech</a></li><li><a href="https://eevis.codes/blog/2022-11-06/hey-tech-recruiter-here-are-some-tips-from-a-developer/#give-me-all-the-details"> Give Me All the Details</a></li><li><a href="https://eevis.codes/blog/2022-11-06/hey-tech-recruiter-here-are-some-tips-from-a-developer/#be-honest-about-the-process"> Be Honest About the Process</a></li><li><a href="https://eevis.codes/blog/2022-11-06/hey-tech-recruiter-here-are-some-tips-from-a-developer/#do-what-youve-promised-and-communicate-if-you-cant"> Do What You've Promised - And Communicate If You Can't</a></li><li><a href="https://eevis.codes/blog/2022-11-06/hey-tech-recruiter-here-are-some-tips-from-a-developer/#treat-juniors-and-people-from-minorities-in-tech-with-respect"> Treat Juniors and People From Minorities in Tech with Respect</a></li><li><a href="https://eevis.codes/blog/2022-11-06/hey-tech-recruiter-here-are-some-tips-from-a-developer/#check-your-websites-accessibility"> Check Your Website's Accessibility</a></li><li><a href="https://eevis.codes/blog/2022-11-06/hey-tech-recruiter-here-are-some-tips-from-a-developer/#check-your-websites-representation"> Check Your Website's Representation</a></li><li><a href="https://eevis.codes/blog/2022-11-06/hey-tech-recruiter-here-are-some-tips-from-a-developer/#wrapping-up"> Wrapping Up</a></li>
</ol>
</nav>
<p>I wrote a rant to LinkedIn in the summer, summarizing a couple of tips on how (not) to contact me and similar stuff. Since then, I've been thinking about writing a blog post about this topic, and here it finally is!</p>
<p>I've had good experiences with tech recruiters over the past years, but unfortunately, there have also been bad experiences. And this blog post comes from those occasions.</p>
<p>Here's the original rant: </p>
<blockquote cite="https://www.linkedin.com/posts/eevajonna_dear-tech-recruiter-here-are-a-couple-of-activity-6953278988425330688-pN9y">
<p>Dear tech recruiter, here are a couple of things I'd wish from you.</p>
<ul>
<li>✨ Please don't call me. Even though you can find my number somewhere, it's not an open invitation to call. I hate speaking on the phone, so you calling me unsolicitedly makes me anxious. And I don't want to work for your company or your client if you make me anxious.</li>
<li>✨ Please don't call me many times. You know, the seventh call within three days is just too much. (The first one was too, but… seventh. C'moon.)</li>
<li>✨ If you say you're a tech recruiter, you really should know the difference between frontend, backend, and devops. And you should look at my profile at least just enough to know I'm a frontend developer - and not contact me with backend / devops-positions.</li>
<li>✨ Oh, and a tip: Java and JavaScript are different languages. Me having JavaScript on my profile does not mean I have extensive knowledge on Java.</li>
<li>✨ I won't answer "Hey I have this mysterious client you would be lucky to work for"-type of messages. Sorry, I know you always can't disclose the client, but I want to know where I'm getting into if I take a call with someone.</li></ul>
<p>And a ✨Bonus Tip✨: Oh, and if your company's/your client's website has these fancy animations/auto-playing things that make me literally sick I don't want to work for you or your client. I know I'm an accessibility specialist who could come and fix these things, but from the messages I've received, you're not looking for that. You're looking for a backend developer. So… no.</p>
<p>Yes, it's been one of those weeks.</p></blockquote>
<p><a href="https://www.linkedin.com/posts/eevajonna_dear-tech-recruiter-here-are-a-couple-of-activity-6953278988425330688-pN9y">Source: LinkedIn</a></p>
<p>These tips are written from my perspective, and someone might disagree with them. But from what I've discussed with several other developers, they have agreed with me on these tips.</p>
<p>If you find yourself or your company from these examples, I don't assume you're doing it because you want to be evil or something like that. I don't think you're a terrible person because of that. I just wanted to say that. And now we can get to the tips.</p>
<h2 id="dont-call-me">Don't Call Me</h2>
<p>Really, unless we have agreed in a conversation (which was in written form) that it's okay that you call me, don't. I don't like speaking with people I don't know on the phone. Also, due to some issues related to memory, I'd rather have everything written than try to remember details like who that person was, what the company was, etc.</p>
<p>And definitely don't call me multiple times. I won't answer you. I use search engines to find the number, and after I see that the number belongs to a potential recruiter, I write "Don't answer" as a note to it. And you know, seven calls within three work days is just way too much. </p>
<h2 id="know-the-basics-of-tech">Know The Basics of Tech</h2>
<p>If you're a tech recruiter, you should know the basics of tech. I don't mean that you should learn how to code - even though it is fun! No, you should know the difference between frontend, backend, DevOps, mobile, and other similar concepts. You should also have a basic understanding of programming languages at a level that you know that Java is different from JavaScript.</p>
<p>I get these messages fairly often that say, "Hey! We are/our client is looking for someone with extensive knowledge on Java to work with backend." Did you check what my LinkedIn profile says about my skills? I mean, it talks about frontend technologies, accessibility, and JavaScript. Not Java. There's no Java anywhere. </p>
<p>And I know that that is a tactic just to send many messages out there and hope someone answers. However, for every message I receive that's like that, I add the recruiter and company to my mental list. I don't want to work with companies that don't have a basic understanding of tech because it already tells me a lot about the culture.</p>
<h2 id="give-me-all-the-details">Give Me All the Details</h2>
<p>I receive many "I have this mysterious client in this and that sector looking for someone"-messages. Sometimes I might even bother answering and say that I'm not interested in starting a recruitment process with a company I know very little about. </p>
<p>And I understand that it's not always possible to reveal the name of the company, but I'd like to question the whole concept - why it needs to be like that? Having as much information as possible is best for both parties.</p>
<p>Knowing what I'm getting into (even if it's only the first interview or a call with a recruiter) is essential. It's a stressful situation, and if I go in without almost any information, I can't prepare. And I don't like that feeling. I need time to process my answers, and if I don't have any time to think about them before the interview, I will fail, and it's a waste of time for both parties.</p>
<h2 id="be-honest-about-the-process">Be Honest About the Process</h2>
<p>Over the years, I've had several experiences where I agreed to have a meeting to "get to know each other and casually chat." And when that meeting started, the first question was something like, "Why do you want to work at this company?" Like, hey, I don't know if I want to! I'm here to find it out!</p>
<p>And don't get me even started on surprise technical interviews. If the information about the meeting is that we'll casually talk about technical topics, a live coding exercise is not okay. And neither is being a dick about not doing that exercise. (Okay, that was not a recruiter.)</p>
<p>The information and agenda of the meetings need to be known beforehand. I need to prepare for them, and if the agenda is something other than what was agreed upon, I can't prepare. And I feel shitty about that. And if I feel shitty... I probably don't want to work at that company. I am just saying.</p>
<h2 id="do-what-youve-promised---and-communicate-if-you-cant">Do What You've Promised - And Communicate If You Can't</h2>
<p>When a tech recruiter says they'll contact me at a certain time, I assume they'll do that. But I can't even count the times when I've been told that a company would get back to me at a certain date, and there was nothing. Just silence. And when I asked about that, they either ghosted me or treated me as if I should be thankful that they even answered.</p>
<p>It's okay if some things take time. Sometimes people deciding about the next steps are unavailable, and that's life. Sending a short message like "Hey, I know we promised to get back to you today, but I am sorry, [insert reason for the delay], and we'll get back to you [insert new timeline]." shouldn't be too much effort. </p>
<p>And yes, things happen. And it's okay - if you communicate about it. Sending that short message mentioned in the previous paragraph shows respect toward the candidate.</p>
<h2 id="treat-juniors-and-people-from-minorities-in-tech-with-respect">Treat Juniors and People From Minorities in Tech with Respect</h2>
<p>I know we are in an era where everyone wants a senior developer, and there are just not enough of us. And there's the fact that white men are often seen as more competent than others. But if you only treat the ones you would like to hire now with respect and not, e.g., ghost them, it will bite you back.</p>
<p>You know, those juniors will be seniors one day. And those people coming from minorities in tech are as competent as, well, white men. And as the tech scene is small, we also often tell about our experiences to others - seniors as well. </p>
<p>I have many examples where my being treated poorly led to someone more senior than me refusing even an interview with a company because of my experiences. And as I've become more senior, I've also refused to interview because of those experiences. </p>
<p>For many, this is a no-brainer. But from my experience, there are a lot of companies and/or recruiters who don't get that.</p>
<h2 id="check-your-websites-accessibility">Check Your Website's Accessibility</h2>
<p>On Global Accessibility Awareness Day 2021, I got a call from a recruiter. They told me from which company they were, and I opened their website. I was on my computer and had a large screen with a browser in full-screen mode, so everything was big on the site. It contained some pretty wild animations, which triggered my symptoms. I felt very disoriented and tried to get away from the call (I'm too nice to hang up). </p>
<p>I tried to keep it together just enough to explain what had happened - and honestly, I have no idea if I succeeded. After the call, I needed to lie down for over 30 minutes, and I couldn't continue working for a while. </p>
<p>That is my reality with all those fancy parallax scrollings and other effects. I need to be extra careful when going to a new site. And I'm not alone. </p>
<p>Also, there are lots of disabled people who you might want to hire. But if your company's website is not accessible, they either encounter similar things as I do, or they can't use the website at all if it's not accessible. And let me tell you - you probably won't be able to hire us. </p>
<h2 id="check-your-websites-representation">Check Your Website's Representation</h2>
<p>Looking at some companies' websites, the wanted employee consists of these attributes:</p>
<ul>
<li>Man</li>
<li>Plays video games</li>
<li>Drinks beer</li>
<li>Has a technical master's degree (and from a certain university)</li>
</ul>
<p>And you know... I don't fit into that. I'm a woman; I don't play video games because I didn't have the opportunity when I was a child. Nowadays, I get frustrated because everyone else is so much better at them. </p>
<p>I don't drink beer (I rarely drink alcohol at all), and I don't have a technical master's degree - after all, as a developer, I'm self-taught.</p>
<p>And I don't mean that if I don't fit in, then nobody who's from a minority wouldn't. But only some people fit into that. Even if the actual culture inside the company is something different, we, the potential recruits, won't know it. That is if the websites and job advertisements give a one-sided picture of the company and its culture.</p>
<p>So, check the imagery of your company's websites and advertisements. Does everyone in the pictures look the same? Or is there diversity in these images? </p>
<p>Another thing to check about the imagery is that it doesn't echo the idea of white men being the seniors and POC, women, and other minorities in tech as juniors/trainees. I can't count how often I get targeted ads where senior roles have a man in the picture, and for the junior position, there's a woman in the advertisement.</p>
<h2 id="wrapping-up">Wrapping Up</h2>
<p>This blog post might feel a bit sharp-tempered, and you're right; that was the point. I wanted to write these feelings out. I am frustrated with certain types of messages (and calls) I get from recruiters.</p>
<p>However, only some recruiters are like that. I've had many good convos with fantastic recruiters who clearly respect the (potential) candidate and their time. </p>
<p>If you found yourself or your company from one of the points, here's the good news: Now you can learn and do better in the future! It's the best place to be - without learning, we never can get better. </p>
</article>
Android, Animations and Reduced Motion2022-12-12T00:00:00.000Zhttps://eevis.codes/blog/2022-12-12/android-animations-and-reduced-motion/
<nav id="breadcrumbs" aria-label="breadcrumbs">
<ol>
<li>
<a href="https://eevis.codes/blog/">Blog</a>
<span aria-hidden="true" class="breadcrumb-separator">/<span>
</span></span></li>
<li>Android, Animations and Reduced Motion</li><li>
</li></ol>
</nav>
<article class="blogPostLong">
<h1 id="/2022-12-12/android-animations-and-reduced-motion">Android, Animations and Reduced Motion</h1>
<div class="reading-time-and-date">
<p>
Reading time: about 4 minutes
</p>
<span class="separator"></span>
<p>Published <time datetime="12th Dec, 2022">12th Dec, 2022</time></p>
</div>
<section class="likes-and-boosts">
<div>
<svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="boosts-title">
<title id="boosts-title">Boosts</title>
<path d="M7.19 4.35047C5.15 6.64047 3.75 9.93047 3.62 10.2405L0 8.69047L4.05 4.64047C4.52 4.17047 5.2 3.96047 5.86 4.09047L7.19 4.35047ZM9.17 15.0005C9.17 15.0005 12.91 13.4505 15.06 11.3005C20.46 5.90047 19.56 1.68047 19.27 0.730473C18.32 0.430473 14.1 -0.459527 8.7 4.94047C6.55 7.09047 5 10.8305 5 10.8305L9.17 15.0005ZM15.65 12.8105C13.36 14.8505 10.07 16.2505 9.76 16.3805L11.31 20.0005L15.36 15.9505C15.83 15.4805 16.04 14.8005 15.91 14.1405L15.65 12.8105ZM7 16.0005C7 16.8305 6.66 17.5805 6.12 18.1205C4.94 19.3005 0 20.0005 0 20.0005C0 20.0005 0.7 15.0605 1.88 13.8805C2.2988 13.4595 2.83325 13.1724 3.4155 13.0558C3.99775 12.9392 4.60153 12.9982 5.15017 13.2253C5.69882 13.4525 6.16758 13.8376 6.49694 14.3317C6.8263 14.8258 7.0014 15.4067 7 16.0005ZM11 7.00047C11 5.90047 11.9 5.00047 13 5.00047C14.1 5.00047 15 5.90047 15 7.00047C15 8.10047 14.1 9.00047 13 9.00047C11.9 9.00047 11 8.10047 11 7.00047Z" fill="#00696D"></path>
</svg>
0
</div>
<div>
<svg role="img" width="20" height="19" viewBox="0 0 20 19" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="likes-title">
<title id="likes-title">Likes</title>
<path d="M10 18.35L8.55 17.03C3.4 12.36 0 9.27 0 5.5C0 2.41 2.42 0 5.5 0C7.24 0 8.91 0.81 10 2.08C11.09 0.81 12.76 0 14.5 0C17.58 0 20 2.41 20 5.5C20 9.27 16.6 12.36 11.45 17.03L10 18.35Z"></path>
</svg>
0
</div>
</section>
<img class="cover-photo" src="https://images.ctfassets.net/mpqufjsy02zr/1xKIWhmB62VgG1aqZpsFVr/697f97cefb3de65c88a0635b66e8dfb0/android_and_reduced_motion.png" alt="Android, Animations and Reduced Motion." />
<div class="blog-tags">
<a href="https://eevis.codes/tags/Android"><span class="blog-tag blue">Android</span></a> <a href="https://eevis.codes/tags/Kotlin"><span class="blog-tag turquoise">Kotlin</span></a>
</div>
<nav aria-label="Android, Animations and Reduced Motion Table of Contents" class="table-of-contents">
<h2>Table of Contents</h2>
<ol>
<li><a href="https://eevis.codes/blog/2022-12-12/android-animations-and-reduced-motion/#why-is-this-important"> Why is this Important?</a></li><li><a href="https://eevis.codes/blog/2022-12-12/android-animations-and-reduced-motion/#when-to-check-the-reduced-motion-setting-in-your-code"> When to Check the Reduced Motion Setting in Your Code?</a></li><li><a href="https://eevis.codes/blog/2022-12-12/android-animations-and-reduced-motion/#how-to-detect-if-reduced-motion-is-enabled"> How to Detect if Reduced Motion is Enabled</a></li><li><a href="https://eevis.codes/blog/2022-12-12/android-animations-and-reduced-motion/#wrapping-up"> Wrapping Up</a></li><li><a href="https://eevis.codes/blog/2022-12-12/android-animations-and-reduced-motion/#read-more"> Read More </a></li>
</ol>
</nav>
<p>I don't like animations, and I'm not alone. The Verge published an <a href="https://www.theverge.com/23191768/animation-accessibility-neurodivergence">article by s. e. smith: My war on animation</a> as part of their Accessibility week, and I loved it. I was able to relate to that article on so many levels. </p>
<p>For me, animation and motion on a website or app can be a problem for two reasons: They might make me physically sick and distract me. Especially when I'm tired, distraction is a huge problem. </p>
<p>So, now you might wonder, how can I survive the internet today? Animations are everywhere. </p>
<p>There's an app for that! Or, rather, a setting. I can turn motion off (or reduce it) with operating system settings on my phone and computer. When this setting is on, operating system level animations are turned off. </p>
<p>How about websites and apps, then? Do they respect that setting? Yes and no. It entirely depends on the developers implementing the reduced motion-media query on the web. On the other hand, in apps, it depends on how the animations are built.</p>
<p>In this blog post, I'm talking about Android animations and how to respect that setting. I also discuss when you need to implement the check and when the animations are turned off by default.</p>
<p>But first, let's talk a bit about why you should care about respecting that setting.</p>
<h2 id="why-is-this-important">Why is this Important?</h2>
<p>It's the right thing to do. I mean, users have expectations on how apps work, and one of these expectations is that operating system-level settings are respected. </p>
<p>Sometimes not respecting that setting creates inconveniences, but sometimes it's about real, life-affecting symptoms that might get triggered. Nobody enjoys having a migraine or feeling sick after encountering something unexpected in the app - something that could have been prevented with a few lines of code. </p>
<p>I know there's not much knowledge about the need for reduced motion. I've worked with multiple devs, designers, and product people who've had no idea about that. And animations are here to stay - they drive engagement (or that's what everyone keeps telling me), and some people like them. But I just hope "engagement" won't drive over the accessibility needs of the people - so that's why I'm trying to educate everyone about this topic. </p>
<h2 id="when-to-check-the-reduced-motion-setting-in-your-code">When to Check the Reduced Motion Setting in Your Code?</h2>
<p>Reduced motion (or, animations off) -setting affects all Animator-based animations. That means most of the animations you use are turned off by default. The other week, I was developing a feature that required me to turn that setting off because of the animations. I was surprised by how much motion and animation was being turned off by that setting! </p>
<p>Also, when creating animations with Jetpack Compose, its animation classes follow that setting from version 1.2.0. However, if you're using earlier versions, I'd recommend testing if the setting is respected out of the box.</p>
<p>Another case when you need to know how to check if the reduced motion setting is on is if you're using something like Lottie-animations. I love how Lottie's Android component checks for reduced motion and displays the first frame if it's on - and all of this, out of the box. </p>
<p>However, sometimes the first frame is not applicable. Often animations start with an (almost) empty screen, which is not what you want to show users with reduced motion. So in these cases, checking if reduced motion is on and displaying a static image instead is the way to go. </p>
<p>So, there's no need for extra checks in many cases - at least if you're using the latest versions of, e.g., Compose. However, testing is the key. You can turn the reduced motion setting on from the accessibility settings - the setting is called "Remove animations," and depending on your phone, it might be under "Display" or "Visibility enhancements" or other similar. </p>
<h2 id="how-to-detect-if-reduced-motion-is-enabled">How to Detect if Reduced Motion is Enabled</h2>
<p>Since Android doesn't expose the value for reduced motion the same way as other accessibility services, such as if a screen reader is enabled, we need to be a little creative.</p>
<p>One way to deduce if reduced motion is enabled is to check the animation duration scale. We can get that value with </p>
<pre><code class="language-kotlin">Settings<span class="token punctuation">.</span>Global<span class="token punctuation">.</span><span class="token function">getFloat</span><span class="token punctuation">(</span>
context<span class="token punctuation">.</span>contentResolver<span class="token punctuation">,</span>
Settings<span class="token punctuation">.</span>Global<span class="token punctuation">.</span>ANIMATOR_DURATION_SCALE
<span class="token punctuation">)</span>
</code></pre><p>The animation duration scale is <code>0f</code> if reduced motion-setting is enabled. </p>
<p>However, if the reduced motion setting is not enabled, that setting can be undefined. So, to check the value, we'll need a catch-block, where we set the value to basically anything than <code>0f</code>:</p>
<pre><code class="language-kotlin"><span class="token keyword">val</span> animationDuration <span class="token operator">=</span> <span class="token keyword">try</span> <span class="token punctuation">{</span>
Settings<span class="token punctuation">.</span>Global<span class="token punctuation">.</span><span class="token function">getFloat</span><span class="token punctuation">(</span>
context<span class="token punctuation">.</span>contentResolver<span class="token punctuation">,</span>
Settings<span class="token punctuation">.</span>Global<span class="token punctuation">.</span>ANIMATOR_DURATION_SCALE
<span class="token punctuation">)</span>
<span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span>e<span class="token operator">:</span> Settings<span class="token punctuation">.</span>SettingNotFoundException<span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token number">1f</span>
<span class="token punctuation">}</span>
</code></pre><p>Finally, to check if reduced motion is enabled, we can check if the animation duration is <code>0f</code>. Here's the complete function for that:</p>
<pre><code class="language-kotlin"><span class="token keyword">fun</span> <span class="token function">isReducedMotionEnabled</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">:</span> Boolean <span class="token punctuation">{</span>
<span class="token keyword">val</span> animationDuration <span class="token operator">=</span> <span class="token keyword">try</span> <span class="token punctuation">{</span>
Settings<span class="token punctuation">.</span>Global<span class="token punctuation">.</span><span class="token function">getFloat</span><span class="token punctuation">(</span>context<span class="token punctuation">.</span>contentResolver<span class="token punctuation">,</span> Settings<span class="token punctuation">.</span>Global<span class="token punctuation">.</span>ANIMATOR_DURATION_SCALE<span class="token punctuation">)</span>
<span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span>e<span class="token operator">:</span> Settings<span class="token punctuation">.</span>SettingNotFoundException<span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token number">1f</span>
<span class="token punctuation">}</span>
<span class="token keyword">return</span> animationDuration <span class="token operator">==</span> <span class="token number">0f</span>
<span class="token punctuation">}</span>
</code></pre><h2 id="wrapping-up">Wrapping Up</h2>
<p>So, even though most animations on Android respect the reduced motion setting out of the box, it's good to be aware of the possibility of situations when it needs to be respected. </p>
<p>I hope you've learned something from this blog post, and in the future, remember that animations are only a delight for some. </p>
<h2 id="read-more">Read More</h2>
<ul>
<li><a href="https://link.springer.com/book/10.1007/978-1-4842-5814-9">Book by Rob Whitaker: Developing Inclusive Mobile Apps</a></li>
<li><a href="https://dev.to/eevajonnapanula/you-make-my-head-spin-reducing-the-motion-on-web-328b">My blog post: You Make My Head Spin - Reducing the Motion on Web</a></li>
<li><a href="https://alistapart.com/article/designing-safer-web-animation-for-motion-sensitivity/">Article by Val Head: Designing Safer Web Animation For Motion Sensitivity</a></li>
<li><a href="https://www.theverge.com/23191768/animation-accessibility-neurodivergence">Article by s. e. smith: My war on animation</a></li>
</ul>
</article>
Year in Review - 2022 Edition2023-01-07T00:00:00.000Zhttps://eevis.codes/blog/2023-01-07/year-in-review-2022-edition/
<nav id="breadcrumbs" aria-label="breadcrumbs">
<ol>
<li>
<a href="https://eevis.codes/blog/">Blog</a>
<span aria-hidden="true" class="breadcrumb-separator">/<span>
</span></span></li>
<li>Year in Review - 2022 Edition</li><li>
</li></ol>
</nav>
<article class="blogPostLong">
<h1 id="/2023-01-07/year-in-review-2022-edition">Year in Review - 2022 Edition</h1>
<div class="reading-time-and-date">
<p>
Reading time: about 6 minutes
</p>
<span class="separator"></span>
<p>Published <time datetime="7th Jan, 2023">7th Jan, 2023</time></p>
</div>
<section class="likes-and-boosts">
<div>
<svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="boosts-title">
<title id="boosts-title">Boosts</title>
<path d="M7.19 4.35047C5.15 6.64047 3.75 9.93047 3.62 10.2405L0 8.69047L4.05 4.64047C4.52 4.17047 5.2 3.96047 5.86 4.09047L7.19 4.35047ZM9.17 15.0005C9.17 15.0005 12.91 13.4505 15.06 11.3005C20.46 5.90047 19.56 1.68047 19.27 0.730473C18.32 0.430473 14.1 -0.459527 8.7 4.94047C6.55 7.09047 5 10.8305 5 10.8305L9.17 15.0005ZM15.65 12.8105C13.36 14.8505 10.07 16.2505 9.76 16.3805L11.31 20.0005L15.36 15.9505C15.83 15.4805 16.04 14.8005 15.91 14.1405L15.65 12.8105ZM7 16.0005C7 16.8305 6.66 17.5805 6.12 18.1205C4.94 19.3005 0 20.0005 0 20.0005C0 20.0005 0.7 15.0605 1.88 13.8805C2.2988 13.4595 2.83325 13.1724 3.4155 13.0558C3.99775 12.9392 4.60153 12.9982 5.15017 13.2253C5.69882 13.4525 6.16758 13.8376 6.49694 14.3317C6.8263 14.8258 7.0014 15.4067 7 16.0005ZM11 7.00047C11 5.90047 11.9 5.00047 13 5.00047C14.1 5.00047 15 5.90047 15 7.00047C15 8.10047 14.1 9.00047 13 9.00047C11.9 9.00047 11 8.10047 11 7.00047Z" fill="#00696D"></path>
</svg>
0
</div>
<div>
<svg role="img" width="20" height="19" viewBox="0 0 20 19" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="likes-title">
<title id="likes-title">Likes</title>
<path d="M10 18.35L8.55 17.03C3.4 12.36 0 9.27 0 5.5C0 2.41 2.42 0 5.5 0C7.24 0 8.91 0.81 10 2.08C11.09 0.81 12.76 0 14.5 0C17.58 0 20 2.41 20 5.5C20 9.27 16.6 12.36 11.45 17.03L10 18.35Z"></path>
</svg>
0
</div>
</section>
<img class="cover-photo" src="https://images.ctfassets.net/mpqufjsy02zr/1m0PzAPleYPdxmSLa1LOOU/ab23f7188ce07b8e2f63acce876a5bf5/year_in_review_2022.png" alt="Year in Review - 2022 Edition" />
<div class="blog-tags">
<a href="https://eevis.codes/tags/yearly-review"><span class="blog-tag turquoise">yearly-review</span></a>
</div>
<nav aria-label="Year in Review - 2022 Edition Table of Contents" class="table-of-contents">
<h2>Table of Contents</h2>
<ol>
<li><a href="https://eevis.codes/blog/2023-01-07/year-in-review-2022-edition/#accomplishments"> Accomplishments</a></li><li><a href="https://eevis.codes/blog/2023-01-07/year-in-review-2022-edition/#blogging"> Blogging</a></li><li><a href="https://eevis.codes/blog/2023-01-07/year-in-review-2022-edition/#speaking"> Speaking</a></li><li><a href="https://eevis.codes/blog/2023-01-07/year-in-review-2022-edition/#projects"> Projects</a></li><li><a href="https://eevis.codes/blog/2023-01-07/year-in-review-2022-edition/#helping-with-theses"> Helping with Theses</a></li><li><a href="https://eevis.codes/blog/2023-01-07/year-in-review-2022-edition/#things-you-probably-didnt-see"> Things You Probably Didn't See</a></li><li><a href="https://eevis.codes/blog/2023-01-07/year-in-review-2022-edition/#whats-next"> What's Next?</a></li><li><a href="https://eevis.codes/blog/2023-01-07/year-in-review-2022-edition/#links"> Links </a></li>
</ol>
</nav>
<p>Another year has come to its end, and it's time to look back and reflect on it. I wrote a similar post last year: <a href="https://eevis.codes/blog/2022-01-01/year-in-review-2021-edition/">Year in Review - 2021 edition</a>, and it was interesting to compare these years.</p>
<p>This blog post reflects on my 2022 - highs and lows, interesting new things, and some hard times. It doesn't include everything, but it has many important things that happened last year. Let's dive in. </p>
<h2 id="accomplishments">Accomplishments</h2>
<p>Looking back on 2022, I can see some real highlights and accomplishments. Many materialized this year and have been in the works for years. I am proud of them, and I've earned them. </p>
<h3 id="wtm-ambassador">WTM Ambassador</h3>
<p>In November, I got a mail that I was selected as Women Techmakers Ambassador. What is WTM Ambassador, you ask? Here's how <a href="https://developers.google.com/womentechmakers/ambassadors">Women Techmakers'</a> site defines the role:</p>
<blockquote>
<p>Women Techmakers Ambassadors are leaders around the world who are passionate about empowering their communities through organizing events, public speaking, creating content, and mentoring. With access to a global community and exclusive resources, Ambassadors are helping build a world where all women can thrive in tech.</p>
</blockquote>
<p>It's an honor and a great recognition of my work in past years. Not everyone gets selected, so it means that I've done something right.</p>
<h3 id="nwita-nominee">NWITA Nominee</h3>
<p>Last year (2021), I was one of the finalists in the Nordic Women in Tech Award's category, Rising Star of the Year, and this year, I was nominated in the category Developer of the Year. I didn't make it to the final five, but the nomination is an honor. </p>
<h3 id="dev-top-author">Dev Top Author</h3>
<p>To my surprise, I got an email that I'm one of this year's Dev's Top Authors. Being recognized for the time and effort I've put in writing my blog feels good. </p>
<h3 id="career-switch">Career Switch</h3>
<p>One of the most significant turns this year has taken is my switch from frontend to Android development. To be honest, I was already a bit tired of frontend things. I already knew so much, and even though there's always something new to learn, I've realized I wanted something new. And Android development has been just that.</p>
<h2 id="blogging">Blogging</h2>
<p>I've written some blog posts this year too. I haven't been as productive as in 2021, but I'm quite happy with what I've written. </p>
<p>Three of my blog posts got to Dev's week's top 7. Interestingly, they were quite different from each other: The first was my International Women's Day post (<a href="https://dev.to/eevajonnapanula/nevertheless-eevis-still-codes-53o0">Nevertheless, Eevis Still Codes</a>), the second was Results of Quick Testing of Documentation Tools' Accessibility, and the third was Hey Tech Recruiter, Here Are Some Tips from a Developer.</p>
<p>My personal favorite posts for this year are:</p>
<article class="blog-embed">
<h3>Results of Quick Testing of Documentation Tools' Accessibility</h3>
<p>Published at <time datetime="2022-08-11">August 11th</time></p>
<a href="https://eevis.codes/blog/2022-08-11/results-of-quick-testing-of-documentation-tools-accessibility/">Read the post Results of Quick Testing of Documentation Tools' Accessibility</a>
</article>
<p>I tested four different documentation tools (Swagger, Read the Docs, Docusaurus, and GitBook) and used some accessibility testing tools and techniques with them. In the blog post, I summarise my findings from these tests. Writing this blog post was fun because I like to do these accessibility tests.</p>
<article class="blog-embed">
<h3>Hey Tech Recruiter, Here Are Some Tips from a Developer</h3>
<p>Published at <time datetime="2022-11-06">November 6th</time></p><a href="https://eevis.codes/blog/2022-11-06/hey-tech-recruiter-here-are-some-tips-from-a-developer/">Read the post Hey Tech Recruiter, Here Are Some Tips from a Developer</a>
</article>
<p>One of the most popular posts for this year was this one. It started as a LinkedIn post, and in November, I finally had the time to write it into a whole blog post. I share some frustrations I've faced when communicating with recruiters, and this blog post resonated with many developers. </p>
<article class="blog-embed">
<h3>Android, Animations and Reduced Motion</h3>
<p>Published at <time datetime="2022-12-12">December 12th</time></p>
<a href="https://eevis.codes/blog/2022-12-12/android-animations-and-reduced-motion/">Read the post Android, Animations and Reduced Motion</a>
</article>
<p>This blog post is the first I've written about Android that's about something other than learning Android development. As the reduced motion/animations off-theme is close to me personally, I wanted to understand how I can respect that setting on Android. And as always, I love to share what I've learned, so I wrote a blog post about it.</p>
<h2 id="speaking">Speaking</h2>
<p>I did some speaking, and for the first time in a while, some talks I gave in person.</p>
<p>I was talking in:</p>
<ul>
<li>Turku <3 Frontend</li>
<li>Women in Tech Summit</li>
<li>React Native EU</li>
<li>React Finland</li>
<li>Tyttöjen päivä</li>
<li>Twig the Code</li>
<li>We are developers live</li>
</ul>
<p>I also had to cancel a couple of events:</p>
<ul>
<li>React Meetup in Helsinki</li>
<li>Modern Frontends</li>
</ul>
<p>The first meetup I've ever spoken at was in January 2020. And as we all know, Covid hit pretty soon after - and as I've continued speaking after that, it's been all remote until spring 2022. The first in-person meetup was Turku <3 Frontend in April, and let me tell you, I was nervous! I had thought that I was way better at speaking in front of a camera, not seeing the listeners, but I surprised myself with how well speaking in front of a live audience went and how good it felt.</p>
<p>The number of events for 2022 was lower than for 2021. However, I'm happy about that, and I'm glad about how my speaking year of 2022 turned out. I wouldn't have the energy for more.</p>
<h2 id="projects">Projects</h2>
<p>This year, I created some (web) projects. The first was neule.art, a visualizer for knitting patterns. It has one pattern (an Icelandic sweater), but I hope I'll have some time and inspiration to add other garments. If you want to read about the progress, I wrote a blog post about it: <a href="https://eevis.codes/blog/2022-06-30/how-i-created-neule-art/">How I created Neule.art</a>. Neule.art is made with Eleventy and doesn't use client-side JavaScript.</p>
<p>Another project was a guest book app for my sister and her friends for their birthday party. It was created with React and used AWS for storing pictures. I wrote a blog post about that project: <a href="https://eevis.codes/blog/2022-08-24/learnings-from-creating-a-guest-book-app/">Learnings From Creating a Guest Book App</a>.</p>
<p>The third project for this year was an accessibility-themed advent calendar. It was created with Eleventy, too, and used Github Actions to reveal each day's content. I've been writing a blog post about it and will publish it probably in January.</p>
<h2 id="helping-with-theses">Helping with Theses</h2>
<p>I've had the honor of helping with three different theses - one master's thesis and two bachelor's theses. My role in the progress has been as an accessibility specialist. Depending on the thesis, I've answered more general or technical questions.</p>
<h2 id="things-you-probably-didnt-see">Things You Probably Didn't See</h2>
<p>I could copy some of the lines from last year's post here. I've been struggling with the same things - I had to take some days off because of burnout symptoms. In the summer, I wrote about the theme a bit: <a href="https://eevis.codes/blog/2022-07-15/on-the-edge-of-burnout-again/">On the Edge of Burnout... Again</a>. </p>
<p>Even though I wrote in the blog post that I'm better now, the following months weren't easy. But after some changes at work (like switching roles to Android development), things have started to look better step by step. The feeling during the weekends has turned into "Monday doesn't suck." I'm even excited about the projects I'm working on and what the future will bring. </p>
<p>I also spent lots of time outdoors. I visited five new national parks and acquired a kayaking instructor title. And on the last day of the year, I ordered my own kayak, which I'm super excited about, and I can't wait for it to arrive. </p>
<h2 id="whats-next">What's Next?</h2>
<p>I've learned not to promise too much - the world changes so fast that I cannot know where I will be in a year. But from what I know now, I will learn and code more Android, I will probably write my master's thesis with an accessibility theme, and I definitely will spend some time in a kayak!</p>
<h2 id="links">Links</h2>
<ul>
<li><a href="https://eevis.codes/blog/2022-01-01/year-in-review-2021-edition/">Year in Review - 2021 edition</a></li>
<li><a href="https://developers.google.com/womentechmakers/ambassadors">Women Techmakers</a></li>
<li><a href="https://dev.to/eevajonnapanula/nevertheless-eevis-still-codes-53o0">Nevertheless, Eevis Still Codes</a></li>
<li><a href="https://eevis.codes/blog/2022-08-11/results-of-quick-testing-of-documentation-tools-accessibility/">Read the post Results of Quick Testing of Documentation Tools' Accessibility</a></li>
<li><a href="https://eevis.codes/blog/2022-11-06/hey-tech-recruiter-here-are-some-tips-from-a-developer/">Read the post Hey Tech Recruiter, Here Are Some Tips from a Developer</a></li>
<li><a href="https://eevis.codes/blog/2022-12-12/android-animations-and-reduced-motion/">Read the post Android, Animations and Reduced Motion</a></li>
<li><a href="https://eevis.codes/blog/2022-06-30/how-i-created-neule-art/">How I created Neule.art</a></li>
<li><a href="https://eevis.codes/blog/2022-08-24/learnings-from-creating-a-guest-book-app/">Learnings From Creating a Guest Book App</a></li>
<li><a href="https://eevis.codes/blog/2022-07-15/on-the-edge-of-burnout-again/">On the Edge of Burnout... Again</a></li>
</ul>
</article>
Automating Advent Calendar with Github Actions and Eleventy2023-01-14T00:00:00.000Zhttps://eevis.codes/blog/2023-01-14/automating-advent-calendar-with-github-actions-and-eleventy/
<nav id="breadcrumbs" aria-label="breadcrumbs">
<ol>
<li>
<a href="https://eevis.codes/blog/">Blog</a>
<span aria-hidden="true" class="breadcrumb-separator">/<span>
</span></span></li>
<li>Automating Advent Calendar with Github Actions and Eleventy</li><li>
</li></ol>
</nav>
<article class="blogPostLong">
<h1 id="/2023-01-14/automating-advent-calendar-with-github-actions-and-eleventy">Automating Advent Calendar with Github Actions and Eleventy</h1>
<div class="reading-time-and-date">
<p>
Reading time: about 5 minutes
</p>
<span class="separator"></span>
<p>Published <time datetime="14th Jan, 2023">14th Jan, 2023</time></p>
</div>
<section class="likes-and-boosts">
<div>
<svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="boosts-title">
<title id="boosts-title">Boosts</title>
<path d="M7.19 4.35047C5.15 6.64047 3.75 9.93047 3.62 10.2405L0 8.69047L4.05 4.64047C4.52 4.17047 5.2 3.96047 5.86 4.09047L7.19 4.35047ZM9.17 15.0005C9.17 15.0005 12.91 13.4505 15.06 11.3005C20.46 5.90047 19.56 1.68047 19.27 0.730473C18.32 0.430473 14.1 -0.459527 8.7 4.94047C6.55 7.09047 5 10.8305 5 10.8305L9.17 15.0005ZM15.65 12.8105C13.36 14.8505 10.07 16.2505 9.76 16.3805L11.31 20.0005L15.36 15.9505C15.83 15.4805 16.04 14.8005 15.91 14.1405L15.65 12.8105ZM7 16.0005C7 16.8305 6.66 17.5805 6.12 18.1205C4.94 19.3005 0 20.0005 0 20.0005C0 20.0005 0.7 15.0605 1.88 13.8805C2.2988 13.4595 2.83325 13.1724 3.4155 13.0558C3.99775 12.9392 4.60153 12.9982 5.15017 13.2253C5.69882 13.4525 6.16758 13.8376 6.49694 14.3317C6.8263 14.8258 7.0014 15.4067 7 16.0005ZM11 7.00047C11 5.90047 11.9 5.00047 13 5.00047C14.1 5.00047 15 5.90047 15 7.00047C15 8.10047 14.1 9.00047 13 9.00047C11.9 9.00047 11 8.10047 11 7.00047Z" fill="#00696D"></path>
</svg>
0
</div>
<div>
<svg role="img" width="20" height="19" viewBox="0 0 20 19" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="likes-title">
<title id="likes-title">Likes</title>
<path d="M10 18.35L8.55 17.03C3.4 12.36 0 9.27 0 5.5C0 2.41 2.42 0 5.5 0C7.24 0 8.91 0.81 10 2.08C11.09 0.81 12.76 0 14.5 0C17.58 0 20 2.41 20 5.5C20 9.27 16.6 12.36 11.45 17.03L10 18.35Z"></path>
</svg>
0
</div>
</section>
<img class="cover-photo" src="https://images.ctfassets.net/mpqufjsy02zr/4K7jxnPBR1Ul4bJ1mS6yjl/de7f719976f589a129b73af7d4dac33d/advent-calendar.png" alt="Automating Advent Calendar with Github Actions and Eleventy" />
<div class="blog-tags">
<a href="https://eevis.codes/tags/Eleventy"><span class="blog-tag orange">Eleventy</span></a> <a href="https://eevis.codes/tags/webdev"><span class="blog-tag blue">webdev</span></a>
</div>
<nav aria-label="Automating Advent Calendar with Github Actions and Eleventy Table of Contents" class="table-of-contents">
<h2>Table of Contents</h2>
<ol>
<li><a href="https://eevis.codes/blog/2023-01-14/automating-advent-calendar-with-github-actions-and-eleventy/#the-stack"> The Stack</a></li><li><a href="https://eevis.codes/blog/2023-01-14/automating-advent-calendar-with-github-actions-and-eleventy/#hiding-content-with-eleventy"> Hiding Content with Eleventy</a></li><li><a href="https://eevis.codes/blog/2023-01-14/automating-advent-calendar-with-github-actions-and-eleventy/#github-actions"> Github Actions </a></li><li><a href="https://eevis.codes/blog/2023-01-14/automating-advent-calendar-with-github-actions-and-eleventy/#the-content"> The Content</a></li><li><a href="https://eevis.codes/blog/2023-01-14/automating-advent-calendar-with-github-actions-and-eleventy/#sharing-the-calendar"> Sharing the Calendar</a></li><li><a href="https://eevis.codes/blog/2023-01-14/automating-advent-calendar-with-github-actions-and-eleventy/#wrapping-up"> Wrapping Up</a></li>
</ol>
</nav>
<p>I've always seen advent/Christmas calendars as an interesting, technical challenge. How to build something that hides content until some specified time? And to do that without client-side JavaScript? And automatically, so I don't need to deploy the page daily manually? </p>
<p>Last year, I decided to take up the challenge and build an advent calendar. After some consideration, the concept was clear: it would be in Finnish and about accessibility. I have a Finnish Instagram account where I talk about accessibility, so this combination made the most sense. </p>
<p>After that, there was all the technical decisions and solving the problem above: How to keep content hidden until the right time? In this blog post, I'll share how I solved that problem. But first, let's look into the stack I chose to use.</p>
<h2 id="the-stack">The Stack</h2>
<p>The calendar is built with Eleventy - my go-to choice when I want to create something that's not so complicated that I need to break it into smaller components. Actually, now that I think of it, I'd still take Eleventy - my projects are usually simple. </p>
<p>Other than that, I'm using plain CSS and no client-side JavaScript. I love how you can do that with Eleventy. So, if you're wondering how I'm doing "not showing the content before the correct date": No, it's not with JavaScript. It's a combination of Eleventy's features, a private repository, and Github Actions. Let's talk about them next.</p>
<h2 id="hiding-content-with-eleventy">Hiding Content with Eleventy</h2>
<p>I spent quite some time trying to come up with a solution that would solve my problem. I could have used client-side JavaScript, but then again, if I could solve that without it, it would be great.</p>
<p>So I started doing some research. Is there a way to prevent some pages from getting into the build Eleventy does? After searching with some terms that did not give me good results, I finally found the solution: <code>.eleventyignore</code> and having an <code>unpublished</code>-directory containing the unpublished content. This way, the contents of the <code>unpublished</code>-directory won't be part of the build.</p>
<p>Yes, I felt a bit stupid after discovering this ignore file because that was not the first thing I thought. I mean, every JS project and package has one, so why didn't I think about it straight away? Well, that happens. I am sharing this because, usually, these blog posts are about results, not progress.</p>
<p>Back to the story, I now had a way to hide the following days. The next thing was to write a script to move the current day's content from one folder (<code>unpublished</code>) to another (<code>2022</code>).</p>
<p>As I've been using Node for many years, I used it for that. I could have gone with the shell commands, but my shell scripting skills are way rustier than Node skills, so I went with the easiest solution. </p>
<p>After a while, I had a small function to handle moving the file. The function takes the current date as a parameter. It checks if the <code>unpublished</code> folder contains a file with that date and moves it to the <code>2022</code>-folder.</p>
<p>The next thing was to set up Github Action to run the script daily, commit it to the main branch and rebuild the site.</p>
<h2 id="github-actions">Github Actions</h2>
<p>For the calendar app to work correctly, there were three noteworthy aspects of writing the Github Action file:
Running the workflow daily
Running the Node script mentioned in the previous section
Committing changes to the main branch. </p>
<p>For running the workflow daily, I already had an example of a schedule-event with cron in another project, so that was a little copy-paste. <a href="https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows">Github has good documentation around triggering events on workflows</a>, and I wanted to run the workflow every night at 00:00 UTC, so these are the lines I used:</p>
<pre><code class="language-yaml"> <span class="token key atrule">schedule</span><span class="token punctuation">:</span>
<span class="token punctuation">-</span> <span class="token key atrule">cron</span><span class="token punctuation">:</span> <span class="token string">'0 0 * * *'</span>
</code></pre><p>The second task, running the Node script, was also straightforward. Defining the script in <code>package.json</code>´s scripts made it effortless to run in the workflow.</p>
<p>I used the shell scripts for the third task because I had an example of that in another project. I first defined the committers email and name with</p>
<pre><code class="language-shell"><span class="token function">git</span> config --global user.email <span class="token string">"updater-bot"</span>
<span class="token function">git</span> config --global user.name <span class="token string">"updater-bot"</span>
</code></pre><p>And after that, I added the moved files and committed them with</p>
<pre><code class="language-shell"><span class="token function">git</span> <span class="token function">add</span> <span class="token number">2022</span> unpublished
<span class="token function">git</span> commit -m <span class="token string">"Move day"</span>
</code></pre><p>and finally pushed the branch to the main with</p>
<pre><code class="language-shell"><span class="token function">git</span> push origin main
</code></pre><p>When the commit hit the main branch, the project got rebuilt, and the new changes were published.</p>
<h2 id="the-content">The Content</h2>
<p>Writing the content was the most fun and time-consuming part of the project. I decided to go with three types of content: Information, task, and book recommendation. </p>
<p>I ended up having 14 days of information, seven days of tasks, and three book recommendations. My goal was to gather material from different sources, with different types, and mainly in Finnish. And as I wanted to provide something new for a wider audience, I avoided technical content. </p>
<h2 id="sharing-the-calendar">Sharing the Calendar</h2>
<p>At the beginning of December, I shared the calendar on my social media accounts: Instagram and LinkedIn. The initial response was tremendous and showed in analytics; there was a clear spike in the visits to the page. Looking at the analytics, there were shares on (assumed) work settings, as some traffic came from Slack and Teams. </p>
<p>After the initial sharing, I shared each day's content daily on my Instagram account. I could have advertised the calendar way more, but my December was so busy at work, and I didn't have the energy. It shows in the analytics; most of the traffic came from Instagram.</p>
<h2 id="wrapping-up">Wrapping Up</h2>
<p>This project was a fun one to complete. It was also the first web project I've created since switching to Android development, so I was interested in seeing if I'd totally forgotten how to web. I hadn't - but I still like Kotlin more than JavaScript </p>
<p>Do you have technical challenges you've been thinking about for a longer time and want to solve? Or something seen as an intriguing challenge (but not necessarily wanting to solve yourself)?</p>
<p>And oh, if you want to see the calendar, it's <a href="https://saavutettavuusjoulukalenteri.eevis.codes/">Saavutettavuusjoulukalenteri</a>.</p>
</article>
Sometimes I Feel Like I'm Invisible - Experiences of a Woman in Tech2023-02-05T00:00:00.000Zhttps://eevis.codes/blog/2023-02-05/sometimes-i-feel-like-im-invisible-experiences-of-a-woman-in-tech/
<nav id="breadcrumbs" aria-label="breadcrumbs">
<ol>
<li>
<a href="https://eevis.codes/blog/">Blog</a>
<span aria-hidden="true" class="breadcrumb-separator">/<span>
</span></span></li>
<li>Sometimes I Feel Like I'm Invisible - Experiences of a Woman in Tech</li><li>
</li></ol>
</nav>
<article class="blogPostLong">
<h1 id="/2023-02-05/sometimes-i-feel-like-im-invisible-experiences-of-a-woman-in-tech">Sometimes I Feel Like I'm Invisible - Experiences of a Woman in Tech</h1>
<div class="reading-time-and-date">
<p>
Reading time: about 6 minutes
</p>
<span class="separator"></span>
<p>Published <time datetime="5th Feb, 2023">5th Feb, 2023</time></p>
</div>
<section class="likes-and-boosts">
<div>
<svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="boosts-title">
<title id="boosts-title">Boosts</title>
<path d="M7.19 4.35047C5.15 6.64047 3.75 9.93047 3.62 10.2405L0 8.69047L4.05 4.64047C4.52 4.17047 5.2 3.96047 5.86 4.09047L7.19 4.35047ZM9.17 15.0005C9.17 15.0005 12.91 13.4505 15.06 11.3005C20.46 5.90047 19.56 1.68047 19.27 0.730473C18.32 0.430473 14.1 -0.459527 8.7 4.94047C6.55 7.09047 5 10.8305 5 10.8305L9.17 15.0005ZM15.65 12.8105C13.36 14.8505 10.07 16.2505 9.76 16.3805L11.31 20.0005L15.36 15.9505C15.83 15.4805 16.04 14.8005 15.91 14.1405L15.65 12.8105ZM7 16.0005C7 16.8305 6.66 17.5805 6.12 18.1205C4.94 19.3005 0 20.0005 0 20.0005C0 20.0005 0.7 15.0605 1.88 13.8805C2.2988 13.4595 2.83325 13.1724 3.4155 13.0558C3.99775 12.9392 4.60153 12.9982 5.15017 13.2253C5.69882 13.4525 6.16758 13.8376 6.49694 14.3317C6.8263 14.8258 7.0014 15.4067 7 16.0005ZM11 7.00047C11 5.90047 11.9 5.00047 13 5.00047C14.1 5.00047 15 5.90047 15 7.00047C15 8.10047 14.1 9.00047 13 9.00047C11.9 9.00047 11 8.10047 11 7.00047Z" fill="#00696D"></path>
</svg>
0
</div>
<div>
<svg role="img" width="20" height="19" viewBox="0 0 20 19" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="likes-title">
<title id="likes-title">Likes</title>
<path d="M10 18.35L8.55 17.03C3.4 12.36 0 9.27 0 5.5C0 2.41 2.42 0 5.5 0C7.24 0 8.91 0.81 10 2.08C11.09 0.81 12.76 0 14.5 0C17.58 0 20 2.41 20 5.5C20 9.27 16.6 12.36 11.45 17.03L10 18.35Z"></path>
</svg>
0
</div>
</section>
<img class="cover-photo" src="https://images.ctfassets.net/mpqufjsy02zr/7IVPGIOMJrKd5X9Mf98sIB/8a69dbc6372eec43dde00ab24acdf919/invisible.png" alt="Sometimes I feel like I'm invisible - experiences of a woman in tech." />
<div class="blog-tags">
<a href="https://eevis.codes/tags/DEI"><span class="blog-tag blue">DEI</span></a> <a href="https://eevis.codes/tags/career"><span class="blog-tag purple">career</span></a>
</div>
<nav aria-label="Sometimes I Feel Like I'm Invisible - Experiences of a Woman in Tech Table of Contents" class="table-of-contents">
<h2>Table of Contents</h2>
<ol>
<li><a href="https://eevis.codes/blog/2023-02-05/sometimes-i-feel-like-im-invisible-experiences-of-a-woman-in-tech/#microaggressions"> Microaggressions</a></li><li><a href="https://eevis.codes/blog/2023-02-05/sometimes-i-feel-like-im-invisible-experiences-of-a-woman-in-tech/#the-meaning-of-allyship"> The Meaning of Allyship</a></li><li><a href="https://eevis.codes/blog/2023-02-05/sometimes-i-feel-like-im-invisible-experiences-of-a-woman-in-tech/#wrapping-up"> Wrapping Up</a></li><li><a href="https://eevis.codes/blog/2023-02-05/sometimes-i-feel-like-im-invisible-experiences-of-a-woman-in-tech/#links"> Links</a></li>
</ol>
</nav>
<p><em>This blog post is very personal and sharing it makes me very vulnerable. So please, respect that. And please, just please don't come and tell me it's all my fault or that I have imagined it all. Because I haven't.</em></p>
<p>Throughout my career, I've felt invisible. It's these small things - My man colleague being asked about a feature, even though I'm leading its development, or someone forgetting to add my team of one into their slide deck where every other team in the company is included. Or when the men in the room constantly speak over me. Or when it's known that I'm the most knowledgeable person on a certain topic, and still a man specifically asking another man in the group to answer his advanced questions on that topic.</p>
<p>As an isolated incident, it's not that bad - I can understand that communication hasn't worked as it should have, and someone didn't know I was responsible or that my team even existed. Or that a man just happened to ask another man this time. </p>
<p>But when it happens regularly from multiple people, pretty much in every workplace I've ever worked in tech, it hurts. It hurts like hell. I feel like I'm invisible, like I or my work doesn't matter. I feel like I'm fighting against something intangible I cannot win because it's too powerful. </p>
<p>Sometimes, after a long week full of these occasions, which have left me feeling invisible, I can't do anything else but cry because I'm so freaking exhausted. I feel so powerless.</p>
<p>And what happens if I try to speak up? Well, I'm that angry woman - or just dismissed or forgotten. Or I get greeted with, "Well that person clearly didn't mean it that way." Maybe what hurts the most is when someone says (repeatedly) that they're going to do something about it, but they never do anything.</p>
<p>Of course, this doesn't happen from everyone or in every instance, but it has happened regularly enough for me to notice it. I remember my first job in tech and how some people treated me like I didn't know anything and that I never would. At the time, I assumed that that's how it is, but reflecting on those encounters has made me understand that it wasn't me; it was them. </p>
<p>I remember being dismissed, not listened to, and sometimes completely ignored in the jobs following the first. I've been left out of a meeting invite as the only woman in the group. I also remember being called too aggressive and too angry. </p>
<p>And I'm not the only one. Women and other minorities in tech face these same experiences all over the world. And there is a word for it: Microaggressions. </p>
<h2 id="microaggressions">Microaggressions</h2>
<p>Many of the things I've described above are microaggressions. Kevin Nadal, who is a professor in psychology at John Jay College of Criminal Justice, defines microaggressions:</p>
<blockquote>
<p>Microaggressions are defined as the everyday, subtle, intentional — and oftentimes unintentional — interactions or behaviors that communicate some sort of bias toward historically marginalized groups.</p>
<p>The difference between microaggressions and overt discrimination or macroaggressions, is that people who commit microaggressions might not even be aware of them.</p>
</blockquote>
<p>(Source: <a href="https://www.npr.org/2020/06/08/872371063/microaggressions-are-a-big-deal-how-to-talk-them-out-and-when-to-walk-away">Andrew Limbong: Microaggressions are a big deal: How to talk them out and when to walk away</a>)</p>
<p>Jennifer Y. Kim and Alyson Meister have researched microaggressions women face in STEM workplaces. In the article, they first define what microaggressions are and how they can be classified. Let's have a closer look.</p>
<p>Microaggressions can be classified into three types, ranging in levels of subtlety: </p>
<ul>
<li>microassault</li>
<li>microinsult</li>
<li>microinvalidation.</li>
</ul>
<p>The first one, microassault, explicitly demeans women, like, by calling them a "bitch". Microinsults, on the other hand, are more subtle comments or behaviors - such as when a manager calls only man employees to the meeting. In this case, the subtle message is that man's work is more valuable. </p>
<p>The third one, microinvalidation, is comments or behaviors negating women's experiences with gender discrimination. Examples include things like gaslighting ("Sexism is a thing of a past!") and invalidating the lived experience of women. (Source: <a href="https://link.springer.com/article/10.1007/s10551-022-05203-0">Jennifer Y. Kim & Alyson Meister: Microaggressions, Interrupted: The Experience and Effects of Gender Microaggressions for Women in STEM</a>)</p>
<p>Further, they write about problems these microaggressions cause for women in STEM. Problems include threats to woman's identity, as they're subtly challenging their legitimacy as leaders, a burden by the difficulty of decoding microaggressions, and the effects on one's (mental) health - burnout is real. </p>
<p>For the research, they interviewed 39 women leaders in STEM in North America with various backgrounds. From the material, they found five types of microaggressions: </p>
<ul>
<li>Devaluation of technical competence</li>
<li>Devaluation of physical presence</li>
<li>Denial of one's reality</li>
<li>Pathologizing one's character</li>
<li>Pathologizing one's gender</li>
</ul>
<p>The other theme they were researching was the meaning of allyship. Let's talk about that in the next section.</p>
<h2 id="the-meaning-of-allyship">The Meaning of Allyship</h2>
<p>Kim and Meister found in their research that the intervention from allies was essential in keeping women from leaving STEM fields. I couldn't agree more. Reading the research paper, I could also put some feelings into words and understand why an ally stepping up has felt so great.</p>
<p>Usually, allies are defined as someone from the dominant group. In the case of gender and tech, by that definition, allies are often men. It's imperative that members of the dominant group help with the efforts to bring more equality, as the non-dominant group alone can't change the status quo. </p>
<p>However, in their study, Kim and Meister defined allies and ally interventions more broadly, including, e.g., more senior women stepping up in the events of microaggressions. And that's how I mean allyship in this blog post - something anyone can and, depending on the situation (e.g., assuming it's safe), should do.</p>
<p>The types of interventions from allies Kim and Meister found fell into two categories: domain-related and person-related. The first, domain-related, was about acknowledging one's technical skills (e.g., vouching publicly for a woman's technical skills after someone had doubted or minimized them). The second one, person-related, was about acknowledging the microaggressions women face and validating the experiences of women.</p>
<p>From my experience, the interventions of allies have often helped me understand and handle the situation and reframe it in my mind. I've recognized that I wasn't imagining the problem: someone else has noticed it too. And further, as Kim and Meister write, it has interrupted the internalization process - otherwise, I would have internalized the thoughts about me not being competent enough, of me not belonging in tech. </p>
<p>This is the call to action to anyone who can: When you see these microaggressions happening, step up and talk about it - especially if you are a leader or in a more senior position.</p>
<h2 id="wrapping-up">Wrapping Up</h2>
<p>Microaggressions suck. As isolated incidents, they're small things, but when they happen repeatedly, they build this colossal load which can eventually lead to health problems and women and other minorities leaving tech. So we need to do better.</p>
<p>Think about your work environment. What could you do to prevent microaggressions from happening?</p>
<h2 id="links">Links</h2>
<ul>
<li><a href="https://www.npr.org/2020/06/08/872371063/microaggressions-are-a-big-deal-how-to-talk-them-out-and-when-to-walk-away">Andrew Limbong: Microaggressions are a big deal: How to talk them out and when to walk away</a></li>
<li><a href="https://link.springer.com/article/10.1007/s10551-022-05203-0">Jennifer Y. Kim & Alyson Meister: Microaggressions, Interrupted: The Experience and Effects of Gender Microaggressions for Women in STEM</a></li>
</ul>
</article>
Improving Android Accessibility with Modifiers in Jetpack Compose 2023-07-11T00:00:00.000Zhttps://eevis.codes/blog/2023-07-11/improving-android-accessibility-with-modifiers-in-jetpack-compose/
<nav id="breadcrumbs" aria-label="breadcrumbs">
<ol>
<li>
<a href="https://eevis.codes/blog/">Blog</a>
<span aria-hidden="true" class="breadcrumb-separator">/<span>
</span></span></li>
<li>Improving Android Accessibility with Modifiers in Jetpack Compose </li><li>
</li></ol>
</nav>
<article class="blogPostLong">
<h1 id="/2023-07-11/improving-android-accessibility-with-modifiers-in-jetpack-compose">Improving Android Accessibility with Modifiers in Jetpack Compose </h1>
<div class="reading-time-and-date">
<p>
Reading time: about 10 minutes
</p>
<span class="separator"></span>
<p>Published <time datetime="11th Jul, 2023">11th Jul, 2023</time></p>
</div>
<section class="likes-and-boosts">
<div>
<svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="boosts-title">
<title id="boosts-title">Boosts</title>
<path d="M7.19 4.35047C5.15 6.64047 3.75 9.93047 3.62 10.2405L0 8.69047L4.05 4.64047C4.52 4.17047 5.2 3.96047 5.86 4.09047L7.19 4.35047ZM9.17 15.0005C9.17 15.0005 12.91 13.4505 15.06 11.3005C20.46 5.90047 19.56 1.68047 19.27 0.730473C18.32 0.430473 14.1 -0.459527 8.7 4.94047C6.55 7.09047 5 10.8305 5 10.8305L9.17 15.0005ZM15.65 12.8105C13.36 14.8505 10.07 16.2505 9.76 16.3805L11.31 20.0005L15.36 15.9505C15.83 15.4805 16.04 14.8005 15.91 14.1405L15.65 12.8105ZM7 16.0005C7 16.8305 6.66 17.5805 6.12 18.1205C4.94 19.3005 0 20.0005 0 20.0005C0 20.0005 0.7 15.0605 1.88 13.8805C2.2988 13.4595 2.83325 13.1724 3.4155 13.0558C3.99775 12.9392 4.60153 12.9982 5.15017 13.2253C5.69882 13.4525 6.16758 13.8376 6.49694 14.3317C6.8263 14.8258 7.0014 15.4067 7 16.0005ZM11 7.00047C11 5.90047 11.9 5.00047 13 5.00047C14.1 5.00047 15 5.90047 15 7.00047C15 8.10047 14.1 9.00047 13 9.00047C11.9 9.00047 11 8.10047 11 7.00047Z" fill="#00696D"></path>
</svg>
0
</div>
<div>
<svg role="img" width="20" height="19" viewBox="0 0 20 19" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="likes-title">
<title id="likes-title">Likes</title>
<path d="M10 18.35L8.55 17.03C3.4 12.36 0 9.27 0 5.5C0 2.41 2.42 0 5.5 0C7.24 0 8.91 0.81 10 2.08C11.09 0.81 12.76 0 14.5 0C17.58 0 20 2.41 20 5.5C20 9.27 16.6 12.36 11.45 17.03L10 18.35Z"></path>
</svg>
0
</div>
</section>
<img class="cover-photo" src="https://images.ctfassets.net/mpqufjsy02zr/5LoaFBDSmX0FosTf4M1wpd/5000ad0bbd52306af6129777e83d3541/modifiers_accessibility__1_.png" alt="Improving Android Accessibility with Modifiers in Jetpack Compose" />
<div class="blog-tags">
<a href="https://eevis.codes/tags/Android"><span class="blog-tag blue">Android</span></a> <a href="https://eevis.codes/tags/Kotlin"><span class="blog-tag turquoise">Kotlin</span></a> <a href="https://eevis.codes/tags/a11y"><span class="blog-tag turquoise">a11y</span></a>
</div>
<nav aria-label="Improving Android Accessibility with Modifiers in Jetpack Compose Table of Contents" class="table-of-contents">
<h2>Table of Contents</h2>
<ol>
<li><a href="https://eevis.codes/blog/2023-07-11/improving-android-accessibility-with-modifiers-in-jetpack-compose/#what-are-modifiers"> What are Modifiers?</a></li><li><a href="https://eevis.codes/blog/2023-07-11/improving-android-accessibility-with-modifiers-in-jetpack-compose/#modifiers-for-accessibility"> Modifiers for Accessibility</a></li><li><a href="https://eevis.codes/blog/2023-07-11/improving-android-accessibility-with-modifiers-in-jetpack-compose/#wrapping-up"> Wrapping Up</a></li>
</ol>
</nav>
<p>The other day, I was doing some accessibility fixes on our codebase. I came across a switch that didn't have a label associated with it, meaning screen reader users would need to do a bit of guessing to get the information on what they were toggling. I wanted to fix that and started researching. </p>
<p>At one point, I was going through the list of modifiers for composable components and noticed that many could help make the app more accessible. I got so excited! And I wanted to share some of these modifiers with you, so here we are. I'm going to go through these modifiers: </p>
<ul>
<li><code>toggleable</code></li>
<li><code>selectable</code></li>
<li><code>clickable</code></li>
<li><code>magnifier</code></li>
</ul>
<p>But first, let's talk a bit about modifiers in general and what they are.</p>
<h2 id="what-are-modifiers">What are Modifiers?</h2>
<p>As the Android Developers site defines modifiers:</p>
<blockquote>
<p>Modifiers allow you to decorate or augment a composable. Modifiers let you do these sorts of things:</p>
<ul>
<li>Change the composable's size, layout, behavior, and appearance</li>
<li>Add information, like accessibility labels</li>
<li>Process user input</li>
<li>Add high-level interactions, like making an element clickable,
scrollable, draggable, or zoomable</li>
</ul>
</blockquote>
<p>(Source: <a href="https://developer.android.com/jetpack/compose/modifiers">Android Developers: Compose modifiers</a>)</p>
<p>So they are a way to modify composables' looks, behavior, and semantics. They're used a lot in modern Android development with Jetpack Compose, so if you've ever tried Compose, you've probably come across them.</p>
<p>As the modifiers can do many things, we can use them for improving accessibility as well. Let's talk about that more in the next section.</p>
<h2 id="modifiers-for-accessibility">Modifiers for Accessibility</h2>
<p>There are many use cases for different modifiers to improve the accessibility of our apps. For example, with modifiers, we can reduce the amount of tab stops for users that use switch devices, hardware keyboards, or screen readers, or we can add more semantic information for assistive technology users in general. </p>
<p>We can add visual cues such as focus indicators or improve the visibility and feedback of touch interactions. A good example is what we will do later in this post by increasing the touch target size; we're also increasing the size of the ripple effect to give better feedback on actions to anyone using the app. </p>
<p>One thing I always want to emphasize when discussing accessibility is that when building your app, remember to test it with different assistive technologies. You might find that some things are not working as expected - that's precisely what happened to me when I was writing the code for this blog post.</p>
<p>Oh, and speaking about the code, if you want to see it, you can find <a href="https://github.com/eevajonnapanula/modifiers-example-app">the repository for Modifiers Example App here.</a></p>
<p>In this section, we will look closer at the modifiers I listed at the beginning of the blog post. Let's start with <code>clickable.</code></p>
<h3 id="clickable">Clickable</h3>
<p>Clickable-modifier, as the name states, makes the element clickable. This utility can be useful when creating, e.g., lists of items that need to be clickable or if some larger area needs to be clickable. </p>
<p>However, if you're creating a plain ol' button, I advise using the <code>Button</code>-component because it already has the semantics and behaviors built in and is accessible by default. </p>
<p>Let's consider an example of a list where the user needs to be able to bookmark an item on the list. This picture shows what we are building:</p>
<p><img src="https://images.ctfassets.net/mpqufjsy02zr/26Mu60wqpzb0ghGHeF7ynS/4f320375013d34fdb4b539d042c09839/Screenshot_2023-07-10_at_8.57.48.png" alt="A pink rectangle with rounded corners, which has the text "Bookmark this item" on the left side, and at the end (right side), there is an outlined icon representing a bookmark." /></p>
<p>As seen in the picture, there is a row with text ("Bookmark this item") and a bookmark icon. One option is to wrap the icon with the <code>IconButton</code> component:</p>
<pre><code class="language-kotlin"><span class="token function">Row</span><span class="token punctuation">(</span>
modifier <span class="token operator">=</span> Modifier
<span class="token punctuation">.</span><span class="token function">fillMaxWidth</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">clip</span><span class="token punctuation">(</span>MaterialTheme<span class="token punctuation">.</span>shapes<span class="token punctuation">.</span>large<span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">background</span><span class="token punctuation">(</span>MaterialTheme<span class="token punctuation">.</span>colorScheme<span class="token punctuation">.</span>primaryContainer<span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">padding</span><span class="token punctuation">(</span>ClickableScreen<span class="token punctuation">.</span>padding<span class="token punctuation">)</span><span class="token punctuation">,</span>
horizontalArrangement <span class="token operator">=</span> Arrangement<span class="token punctuation">.</span>SpaceBetween<span class="token punctuation">,</span>
verticalAlignment <span class="token operator">=</span> Alignment<span class="token punctuation">.</span>CenterVertically<span class="token punctuation">,</span>
<span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token function">Text</span><span class="token punctuation">(</span>
text <span class="token operator">=</span> <span class="token string-literal singleline"><span class="token string">"Bookmark this item"</span></span><span class="token punctuation">,</span>
style <span class="token operator">=</span> MaterialTheme<span class="token punctuation">.</span>typography<span class="token punctuation">.</span>titleMedium<span class="token punctuation">,</span>
color <span class="token operator">=</span> MaterialTheme<span class="token punctuation">.</span>colorScheme<span class="token punctuation">.</span>onPrimaryContainer
<span class="token punctuation">)</span>
<span class="token function">IconButton</span><span class="token punctuation">(</span>onClick <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token function">onClick</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token function">FavouriteIcon</span><span class="token punctuation">(</span>
icon <span class="token operator">=</span> icon<span class="token punctuation">,</span>
contentDescription <span class="token operator">=</span> <span class="token string-literal singleline"><span class="token string">"Bookmark this item"</span></span>
<span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>This code snippet would work, and when a user taps the icon button, the item would get bookmarked. However, the area where the user can tap is small. It would be easier to tap the whole row.</p>
<p>Let's make a couple of changes: </p>
<pre><code class="language-kotlin"><span class="token function">Row</span><span class="token punctuation">(</span>
modifier <span class="token operator">=</span> Modifier
<span class="token punctuation">.</span><span class="token function">fillMaxWidth</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">clip</span><span class="token punctuation">(</span>MaterialTheme<span class="token punctuation">.</span>shapes<span class="token punctuation">.</span>large<span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">background</span><span class="token punctuation">(</span>MaterialTheme<span class="token punctuation">.</span>colorScheme<span class="token punctuation">.</span>primaryContainer<span class="token punctuation">)</span>
<span class="token comment">// Add clickable-modifier with the role of Button</span>
<span class="token punctuation">.</span><span class="token function">clickable</span><span class="token punctuation">(</span>
role <span class="token operator">=</span> Role<span class="token punctuation">.</span>Button<span class="token punctuation">,</span>
<span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token function">onClick</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token punctuation">.</span><span class="token function">padding</span><span class="token punctuation">(</span>ClickableScreen<span class="token punctuation">.</span>padding<span class="token punctuation">)</span><span class="token punctuation">,</span>
horizontalArrangement <span class="token operator">=</span> Arrangement<span class="token punctuation">.</span>SpaceBetween<span class="token punctuation">,</span>
verticalAlignment <span class="token operator">=</span> Alignment<span class="token punctuation">.</span>CenterVertically<span class="token punctuation">,</span>
<span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token function">Text</span><span class="token punctuation">(</span>
text <span class="token operator">=</span> <span class="token string-literal singleline"><span class="token string">"Bookmark this item"</span></span><span class="token punctuation">,</span>
style <span class="token operator">=</span> MaterialTheme<span class="token punctuation">.</span>typography<span class="token punctuation">.</span>titleMedium<span class="token punctuation">,</span>
color <span class="token operator">=</span> MaterialTheme<span class="token punctuation">.</span>colorScheme<span class="token punctuation">.</span>onPrimaryContainer
<span class="token punctuation">)</span>
<span class="token comment">// Remove IconButton. </span>
<span class="token comment">// The content description is not needed anymore </span>
<span class="token comment">// as the whole element is merged and text serves as a label </span>
<span class="token function">FavouriteIcon</span><span class="token punctuation">(</span>icon <span class="token operator">=</span> icon<span class="token punctuation">,</span> modifier <span class="token operator">=</span> Modifier<span class="token punctuation">.</span><span class="token function">padding</span><span class="token punctuation">(</span><span class="token number">12</span><span class="token punctuation">.</span>dp<span class="token punctuation">)</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
</code></pre><p>This change improves the accessibility of the modifier a lot. It increases the touch target size and adds the role of a button so that assistive technology users will know it's a button. </p>
<p>This modifier works well if you need to perform a single click. However, if you'll need a double click and/or a long click in addition to a single click, <code>combinedClickable</code> is your friend. </p>
<h3 id="selectable">Selectable</h3>
<p>Another modifier we can use for improving accessibility and usability is <code>selectable.</code> It's often used as part of a mutually exclusive group - such as a radio button group. </p>
<p>There are a couple of benefits to wrapping the whole row with this modifier (instead of, e.g., using a button or <code>clickable</code>-modifier): The area where a user can tap and select the item is larger, you can add correct semantics for the element so that assistive technology users get relevant information and the items can be grouped easily. </p>
<p>Let's talk about how to improve an example I've seen in several places. The picture shows what we're building:</p>
<p><img src="https://images.ctfassets.net/mpqufjsy02zr/QOF0xGo0R4jgbzEc3Iz1Y/7b7cccb2c8a38424738c903389645046/Screenshot_2023-07-10_at_8.58.00.png" alt="Two rows with pink background, text on the left, and a radio button on the right. The first one is selected and has the text "Option A," and the second one is not and has the text "Option B"" /></p>
<p>And here's the simplified code (not copied from any of the codebases mentioned above):</p>
<pre><code class="language-kotlin"><span class="token annotation builtin">@Composable</span>
<span class="token keyword">fun</span> <span class="token function">RadioInputRow</span><span class="token punctuation">(</span>text<span class="token operator">:</span> String<span class="token punctuation">,</span> checked<span class="token operator">:</span> Boolean<span class="token punctuation">,</span> onChange<span class="token operator">:</span> <span class="token punctuation">(</span>String<span class="token punctuation">)</span> <span class="token operator">-></span> Unit<span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token function">Row</span><span class="token punctuation">(</span>
modifier <span class="token operator">=</span> Modifier
<span class="token punctuation">.</span><span class="token function">fillMaxWidth</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">padding</span><span class="token punctuation">(</span><span class="token number">16</span><span class="token punctuation">.</span>dp<span class="token punctuation">)</span><span class="token punctuation">,</span>
horizontalArrangement <span class="token operator">=</span> Arrangement<span class="token punctuation">.</span>SpaceBetween<span class="token punctuation">,</span>
verticalAlignment <span class="token operator">=</span> Alignment<span class="token punctuation">.</span>CenterVertically<span class="token punctuation">,</span>
<span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token function">Text</span><span class="token punctuation">(</span>
text <span class="token operator">=</span> text<span class="token punctuation">,</span>
style <span class="token operator">=</span> MaterialTheme<span class="token punctuation">.</span>typography<span class="token punctuation">.</span>labelLarge<span class="token punctuation">,</span>
color <span class="token operator">=</span> MaterialTheme<span class="token punctuation">.</span>colorScheme<span class="token punctuation">.</span>onPrimaryContainer
<span class="token punctuation">)</span>
<span class="token function">RadioButton</span><span class="token punctuation">(</span>
selected <span class="token operator">=</span> checked<span class="token punctuation">,</span>
onClick <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token function">onChange</span><span class="token punctuation">(</span>text<span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
colors <span class="token operator">=</span> RadioButtonDefaults<span class="token punctuation">.</span><span class="token function">colors</span><span class="token punctuation">(</span>
selectedColor <span class="token operator">=</span> MaterialTheme<span class="token punctuation">.</span>colorScheme<span class="token punctuation">.</span>tertiary<span class="token punctuation">,</span>
unselectedColor <span class="token operator">=</span> MaterialTheme<span class="token punctuation">.</span>colorScheme<span class="token punctuation">.</span>tertiary<span class="token punctuation">,</span>
<span class="token punctuation">)</span><span class="token punctuation">,</span>
<span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>This code snippet works, but a user needs to be able to tap the radio button, which has a small touch target size. Let's make a couple of modifications to make the code more accessible:</p>
<pre><code class="language-kotlin"><span class="token annotation builtin">@Composable</span>
<span class="token keyword">fun</span> <span class="token function">RadioInputRow</span><span class="token punctuation">(</span>
text<span class="token operator">:</span> String<span class="token punctuation">,</span>
checked<span class="token operator">:</span> Boolean<span class="token punctuation">,</span>
onChange<span class="token operator">:</span> <span class="token punctuation">(</span>String<span class="token punctuation">)</span> <span class="token operator">-></span> Unit
<span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token function">Row</span><span class="token punctuation">(</span>
modifier <span class="token operator">=</span> Modifier
<span class="token punctuation">.</span><span class="token function">fillMaxWidth</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token comment">// We add a selectable modifier with state and role to the row</span>
<span class="token punctuation">.</span><span class="token function">selectable</span><span class="token punctuation">(</span>
selected <span class="token operator">=</span> checked<span class="token punctuation">,</span>
enabled <span class="token operator">=</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
role <span class="token operator">=</span> Role<span class="token punctuation">.</span>RadioButton<span class="token punctuation">,</span>
onClick <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token function">onChange</span><span class="token punctuation">(</span>text<span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">padding</span><span class="token punctuation">(</span><span class="token number">16</span><span class="token punctuation">.</span>dp<span class="token punctuation">)</span><span class="token punctuation">,</span>
horizontalArrangement <span class="token operator">=</span> Arrangement<span class="token punctuation">.</span>SpaceBetween<span class="token punctuation">,</span>
verticalAlignment <span class="token operator">=</span> Alignment<span class="token punctuation">.</span>CenterVertically<span class="token punctuation">,</span>
<span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token function">Text</span><span class="token punctuation">(</span>
text <span class="token operator">=</span> text<span class="token punctuation">,</span>
style <span class="token operator">=</span> MaterialTheme<span class="token punctuation">.</span>typography<span class="token punctuation">.</span>labelLarge<span class="token punctuation">,</span>
color <span class="token operator">=</span> MaterialTheme<span class="token punctuation">.</span>colorScheme<span class="token punctuation">.</span>onPrimaryContainer
<span class="token punctuation">)</span>
<span class="token function">RadioButton</span><span class="token punctuation">(</span>
<span class="token comment">// We clear the semantics from</span>
<span class="token comment">// the RadioButton, so that the user, </span>
<span class="token comment">// who is using assistive technology,</span>
<span class="token comment">// doesn't have an extra tab stop on the way.</span>
modifier <span class="token operator">=</span> Modifier<span class="token punctuation">.</span><span class="token function">clearAndSetSemantics</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">,</span>
selected <span class="token operator">=</span> checked<span class="token punctuation">,</span>
onClick <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token function">onChange</span><span class="token punctuation">(</span>text<span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
colors <span class="token operator">=</span> RadioButtonDefaults<span class="token punctuation">.</span><span class="token function">colors</span><span class="token punctuation">(</span>
selectedColor <span class="token operator">=</span> MaterialTheme<span class="token punctuation">.</span>colorScheme<span class="token punctuation">.</span>tertiary<span class="token punctuation">,</span>
unselectedColor <span class="token operator">=</span> MaterialTheme<span class="token punctuation">.</span>colorScheme<span class="token punctuation">.</span>tertiary<span class="token punctuation">,</span>
<span class="token punctuation">)</span><span class="token punctuation">,</span>
<span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>This way, the click area is more extensive, and it's easier to hit and select the target. For this example, I decided to keep the Material Theme's <code>RadioButton</code>-component, but we could also switch it to two icons (selected and unselected), the same way as in the <code>clickable</code>-example. </p>
<p>There are a couple of things to note about the semantics of this example. First, the whole row gets the <code>selectable</code>-modifier, and we pass the state (enabled, selected) to it, and we also pass the role of a switch to it. This way, we can ensure that assistive technology gets correct information about the element, and users will know how to interact with it.</p>
<p>Another aspect related to semantics is to clear all semantics from the original <code>RadioButton</code>-element. If we don't do it, the user will encounter the same component with the same info twice - first on the container (<code>Row</code>) and then with the radio button. That also means an extra tab stop for anyone navigating with assistive technology using focus order.</p>
<p>We want to make one more change and add the <code>selectableGroup</code>-modifier to the parent component that is wrapping all the radio input rows:</p>
<pre><code class="language-kotlin"><span class="token function">Column</span><span class="token punctuation">(</span>
modifier <span class="token operator">=</span> Modifier
<span class="token operator">..</span><span class="token punctuation">.</span>
<span class="token comment">// Adding the selectableGroup here:</span>
<span class="token punctuation">.</span><span class="token function">selectableGroup</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
verticalArrangement <span class="token operator">=</span> Arrangement<span class="token punctuation">.</span>SpaceBetween<span class="token punctuation">,</span>
<span class="token punctuation">)</span> <span class="token punctuation">{</span>
inputs<span class="token punctuation">.</span><span class="token function">map</span> <span class="token punctuation">{</span> input <span class="token operator">-></span>
<span class="token function">RadioInputRow</span><span class="token punctuation">(</span>
text <span class="token operator">=</span> input<span class="token punctuation">,</span>
checked <span class="token operator">=</span> selected <span class="token operator">==</span> input<span class="token punctuation">,</span>
onChange <span class="token operator">=</span> <span class="token punctuation">{</span> selected <span class="token operator">=</span> it <span class="token punctuation">}</span>
<span class="token punctuation">)</span>
<span class="token operator">..</span><span class="token punctuation">.</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>This way, users using, e.g., a screen reader, get the information that there is x number of items in the list as an option to select. </p>
<h3 id="toggleable">Toggleable</h3>
<p>Like <code>selectable,</code> the <code>toggleable</code>- modifier helps make components more accessible. <code>toggleable</code> can be used when there is a state with two values - like selecting a setting with an option to be on or off. </p>
<p>For this example, we have a row with text and a toggle at the end:</p>
<p><img src="https://images.ctfassets.net/mpqufjsy02zr/6WzysUn3rGCcGirnIoFR4m/60ea5cde87aafdea3a6c6e901eef92db/Screenshot_2023-07-10_at_8.58.12.png" alt="A pink rectangle with round corners, and it has the text "Toggleable" at the start (left) side of the row and a switch toggle at the end (right side) of the row." /></p>
<p>And the code we have at first looks like this: </p>
<pre><code class="language-kotlin"><span class="token keyword">fun</span> <span class="token function">SwitchRow</span><span class="token punctuation">(</span>
text<span class="token operator">:</span> String<span class="token punctuation">,</span>
checked<span class="token operator">:</span> Boolean<span class="token punctuation">,</span>
onChange<span class="token operator">:</span> <span class="token punctuation">(</span>Boolean<span class="token punctuation">)</span> <span class="token operator">-></span> Unit
<span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token function">Row</span><span class="token punctuation">(</span>
modifier <span class="token operator">=</span> Modifier
<span class="token punctuation">.</span><span class="token function">fillMaxWidth</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">padding</span><span class="token punctuation">(</span><span class="token number">16</span><span class="token punctuation">.</span>dp<span class="token punctuation">)</span><span class="token punctuation">,</span>
horizontalArrangement <span class="token operator">=</span> Arrangement<span class="token punctuation">.</span>SpaceBetween<span class="token punctuation">,</span>
verticalAlignment <span class="token operator">=</span> Alignment<span class="token punctuation">.</span>CenterVertically<span class="token punctuation">,</span>
<span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token function">Text</span><span class="token punctuation">(</span>
text <span class="token operator">=</span> text<span class="token punctuation">,</span>
style <span class="token operator">=</span> MaterialTheme<span class="token punctuation">.</span>typography<span class="token punctuation">.</span>labelLarge<span class="token punctuation">,</span>
color <span class="token operator">=</span> MaterialTheme<span class="token punctuation">.</span>colorScheme<span class="token punctuation">.</span>onPrimaryContainer
<span class="token punctuation">)</span>
<span class="token function">Switch</span><span class="token punctuation">(</span>
checked <span class="token operator">=</span> checked<span class="token punctuation">,</span>
onCheckedChange <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token function">onChange</span><span class="token punctuation">(</span>it<span class="token punctuation">)</span> <span class="token punctuation">}</span>
<span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>In this code snippet, the touch target is only the area of the Material Theme's <code>Switch</code>-component, as it was with the example of the <code>selectable</code>-modifier. Also, other similar problems (as mentioned in the <code>selectable</code>-section) are present. So, we'll make a couple of changes to make this component more accessible: </p>
<pre><code class="language-kotlin"><span class="token annotation builtin">@Composable</span>
<span class="token keyword">fun</span> <span class="token function">SwitchRow</span><span class="token punctuation">(</span>
text<span class="token operator">:</span> String<span class="token punctuation">,</span>
checked<span class="token operator">:</span> Boolean<span class="token punctuation">,</span>
onChange<span class="token operator">:</span> <span class="token punctuation">(</span>Boolean<span class="token punctuation">)</span> <span class="token operator">-></span> Unit
<span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token function">Row</span><span class="token punctuation">(</span>
modifier <span class="token operator">=</span> Modifier
<span class="token punctuation">.</span><span class="token function">fillMaxWidth</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token comment">// We add the toggleable modifier</span>
<span class="token comment">// with the state (value) and role</span>
<span class="token punctuation">.</span><span class="token function">toggleable</span><span class="token punctuation">(</span>
value <span class="token operator">=</span> checked<span class="token punctuation">,</span>
role <span class="token operator">=</span> Role<span class="token punctuation">.</span>Switch<span class="token punctuation">,</span>
onValueChange <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token function">onChange</span><span class="token punctuation">(</span>it<span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">padding</span><span class="token punctuation">(</span><span class="token number">16</span><span class="token punctuation">.</span>dp<span class="token punctuation">)</span><span class="token punctuation">,</span>
horizontalArrangement <span class="token operator">=</span> Arrangement<span class="token punctuation">.</span>SpaceBetween<span class="token punctuation">,</span>
verticalAlignment <span class="token operator">=</span> Alignment<span class="token punctuation">.</span>CenterVertically<span class="token punctuation">,</span>
<span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token function">Text</span><span class="token punctuation">(</span>
text <span class="token operator">=</span> text<span class="token punctuation">,</span>
style <span class="token operator">=</span> MaterialTheme<span class="token punctuation">.</span>typography<span class="token punctuation">.</span>labelLarge<span class="token punctuation">,</span>
color <span class="token operator">=</span> MaterialTheme<span class="token punctuation">.</span>colorScheme<span class="token punctuation">.</span>onPrimaryContainer
<span class="token punctuation">)</span>
<span class="token function">Switch</span><span class="token punctuation">(</span>
checked <span class="token operator">=</span> checked<span class="token punctuation">,</span>
onCheckedChange <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token function">onChange</span><span class="token punctuation">(</span>it<span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token comment">// We clear the semantics</span>
modifier <span class="token operator">=</span> Modifier<span class="token punctuation">.</span><span class="token function">clearAndSetSemantics</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
<span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>It has a value attribute as part of its state. For <code>toggleable,</code> the role is usually <code>Switch</code> - compared to <code>selectable</code>'s <code>RadioButton.</code> </p>
<h3 id="magnification">Magnification</h3>
<p>The final modifier in this blog post is different from the others. You can use this modifier for magnification in your app. However, it's important to note that in many cases, the operating system's magnification is sufficient, and people who need it know how to use it (and do use it). But this might be useful in some cases, not just for those who use the os's magnification but for anyone who needs to magnify a part of your UI.</p>
<p>For the <code>magnifier</code> modifier to work correctly, <a href="https://developer.android.com/reference/android/widget/Magnifier.html">the Android's Magnifier widget</a> needs to have support on the device. Before proceeding, we'll want to check that:</p>
<pre><code class="language-kotlin"><span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>MagnifierStyle<span class="token punctuation">.</span>Default<span class="token punctuation">.</span>isSupported<span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token function">Text</span><span class="token punctuation">(</span><span class="token string-literal singleline"><span class="token string">"Magnifier is not supported on this platform."</span></span><span class="token punctuation">)</span>
<span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
<span class="token comment">// TODO</span>
<span class="token punctuation">}</span>
</code></pre><p>The <code>magnifier</code>-modifier needs to know the center of the magnified area. We can get that information from the <code>pointerInput</code>-modifier's <code>dragDetectedGestures</code> function. We want to store the value of the center to a variable:</p>
<pre><code class="language-kotlin"><span class="token keyword">var</span> magnifierCenter <span class="token keyword">by</span> remember <span class="token punctuation">{</span>
<span class="token function">mutableStateOf</span><span class="token punctuation">(</span>Offset<span class="token punctuation">.</span>Unspecified<span class="token punctuation">)</span>
<span class="token punctuation">}</span>
</code></pre><p>Then we'll wrap the area we want a user to be able to magnify with a <code>Box</code> element and set the modifiers:</p>
<pre><code class="language-kotlin"><span class="token function">Box</span><span class="token punctuation">(</span>
Modifier
<span class="token comment">// Set the source center and styles for the magnifier</span>
<span class="token punctuation">.</span><span class="token function">magnifier</span><span class="token punctuation">(</span>
sourceCenter <span class="token operator">=</span> <span class="token punctuation">{</span> magnifierCenter <span class="token punctuation">}</span><span class="token punctuation">,</span>
zoom <span class="token operator">=</span> <span class="token number">3f</span><span class="token punctuation">,</span>
style <span class="token operator">=</span> <span class="token function">MagnifierStyle</span><span class="token punctuation">(</span>
size <span class="token operator">=</span> <span class="token function">DpSize</span><span class="token punctuation">(</span>height <span class="token operator">=</span> <span class="token number">200</span><span class="token punctuation">.</span>dp<span class="token punctuation">,</span> width <span class="token operator">=</span> <span class="token number">300</span><span class="token punctuation">.</span>dp<span class="token punctuation">)</span><span class="token punctuation">,</span>
<span class="token punctuation">)</span><span class="token punctuation">,</span>
<span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">pointerInput</span><span class="token punctuation">(</span>Unit<span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token function">detectDragGestures</span><span class="token punctuation">(</span>
<span class="token comment">// Show the magnifier in the initial position</span>
onDragStart <span class="token operator">=</span> <span class="token punctuation">{</span> magnifierCenter <span class="token operator">=</span> it <span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token comment">// Magnifier follows the pointer during a drag event</span>
onDrag <span class="token operator">=</span> <span class="token punctuation">{</span> _<span class="token punctuation">,</span> delta <span class="token operator">-></span>
magnifierCenter <span class="token operator">=</span> magnifierCenter<span class="token punctuation">.</span><span class="token function">plus</span><span class="token punctuation">(</span>delta<span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token comment">// Hide the magnifier when a user ends the drag movement.</span>
onDragEnd <span class="token operator">=</span> <span class="token punctuation">{</span> magnifierCenter <span class="token operator">=</span> Offset<span class="token punctuation">.</span>Unspecified <span class="token punctuation">}</span><span class="token punctuation">,</span>
onDragCancel <span class="token operator">=</span> <span class="token punctuation">{</span> magnifierCenter <span class="token operator">=</span> Offset<span class="token punctuation">.</span>Unspecified <span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token function">Text</span><span class="token punctuation">(</span>
<span class="token string-literal singleline"><span class="token string">"Try magnifying this text by dragging a pointer (finger, mouse, other) over the text."</span></span><span class="token punctuation">,</span>
<span class="token punctuation">)</span>
<span class="token punctuation">}</span>
</code></pre><p>So, with this code snippet, this is what we get: </p>
<p><img src="https://images.ctfassets.net/mpqufjsy02zr/3Ju0id6tNJ2FwheVdjzdjl/46dc39e95d505ac0fb538635de4b8212/magnifier.png" alt="A screen where the magnifier covers part of the text, and the visible text is "t by dragging" and "over the text."" /></p>
<h2 id="wrapping-up">Wrapping Up</h2>
<p>In this blog post, we've discussed modifiers and accessibility in general, and we've looked more closely into four modifiers: <code>clickable,</code> <code>selectable,</code> <code>toggleable,</code> and <code>magnifier.</code> This post was not a comprehensive one on accessibility and modifiers, and I've intentionally left out some topics like focus management and hardware keyboards entirely. I'm planning to write blog posts about those themes later.</p>
<p>Do you have any thoughts, ideas, or questions? Let me know!</p>
</article>
Android Developers and Accessibility - Challenges and Proposed Solutions2023-07-17T00:00:00.000Zhttps://eevis.codes/blog/2023-07-17/android-developers-and-accessibility-challenges-and-proposed-solutions/
<nav id="breadcrumbs" aria-label="breadcrumbs">
<ol>
<li>
<a href="https://eevis.codes/blog/">Blog</a>
<span aria-hidden="true" class="breadcrumb-separator">/<span>
</span></span></li>
<li>Android Developers and Accessibility - Challenges and Proposed Solutions</li><li>
</li></ol>
</nav>
<article class="blogPostLong">
<h1 id="/2023-07-17/android-developers-and-accessibility-challenges-and-proposed-solutions">Android Developers and Accessibility - Challenges and Proposed Solutions</h1>
<div class="reading-time-and-date">
<p>
Reading time: about 7 minutes
</p>
<span class="separator"></span>
<p>Published <time datetime="17th Jul, 2023">17th Jul, 2023</time></p>
</div>
<section class="likes-and-boosts">
<div>
<svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="boosts-title">
<title id="boosts-title">Boosts</title>
<path d="M7.19 4.35047C5.15 6.64047 3.75 9.93047 3.62 10.2405L0 8.69047L4.05 4.64047C4.52 4.17047 5.2 3.96047 5.86 4.09047L7.19 4.35047ZM9.17 15.0005C9.17 15.0005 12.91 13.4505 15.06 11.3005C20.46 5.90047 19.56 1.68047 19.27 0.730473C18.32 0.430473 14.1 -0.459527 8.7 4.94047C6.55 7.09047 5 10.8305 5 10.8305L9.17 15.0005ZM15.65 12.8105C13.36 14.8505 10.07 16.2505 9.76 16.3805L11.31 20.0005L15.36 15.9505C15.83 15.4805 16.04 14.8005 15.91 14.1405L15.65 12.8105ZM7 16.0005C7 16.8305 6.66 17.5805 6.12 18.1205C4.94 19.3005 0 20.0005 0 20.0005C0 20.0005 0.7 15.0605 1.88 13.8805C2.2988 13.4595 2.83325 13.1724 3.4155 13.0558C3.99775 12.9392 4.60153 12.9982 5.15017 13.2253C5.69882 13.4525 6.16758 13.8376 6.49694 14.3317C6.8263 14.8258 7.0014 15.4067 7 16.0005ZM11 7.00047C11 5.90047 11.9 5.00047 13 5.00047C14.1 5.00047 15 5.90047 15 7.00047C15 8.10047 14.1 9.00047 13 9.00047C11.9 9.00047 11 8.10047 11 7.00047Z" fill="#00696D"></path>
</svg>
0
</div>
<div>
<svg role="img" width="20" height="19" viewBox="0 0 20 19" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="likes-title">
<title id="likes-title">Likes</title>
<path d="M10 18.35L8.55 17.03C3.4 12.36 0 9.27 0 5.5C0 2.41 2.42 0 5.5 0C7.24 0 8.91 0.81 10 2.08C11.09 0.81 12.76 0 14.5 0C17.58 0 20 2.41 20 5.5C20 9.27 16.6 12.36 11.45 17.03L10 18.35Z"></path>
</svg>
0
</div>
</section>
<img class="cover-photo" src="https://images.ctfassets.net/mpqufjsy02zr/5sqLmlMCyTYrtJIjT2Mbz2/cd421aa65e171087c22eaa89b68f23eb/challenges-and-solutions__1_.png" alt="Android Developers and Accessibility - Challenges and Proposed Solutions" />
<div class="blog-tags">
<a href="https://eevis.codes/tags/thesis"><span class="blog-tag purple">thesis</span></a> <a href="https://eevis.codes/tags/a11y"><span class="blog-tag turquoise">a11y</span></a> <a href="https://eevis.codes/tags/Android"><span class="blog-tag blue">Android</span></a>
</div>
<nav aria-label="Android Developers and Accessibility - Challenges and Proposed Solutions Table of Contents" class="table-of-contents">
<h2>Table of Contents</h2>
<ol>
<li><a href="https://eevis.codes/blog/2023-07-17/android-developers-and-accessibility-challenges-and-proposed-solutions/#the-challenges"> The Challenges</a></li><li><a href="https://eevis.codes/blog/2023-07-17/android-developers-and-accessibility-challenges-and-proposed-solutions/#proposed-solutions"> Proposed Solutions</a></li><li><a href="https://eevis.codes/blog/2023-07-17/android-developers-and-accessibility-challenges-and-proposed-solutions/#wrapping-up"> Wrapping Up</a></li><li><a href="https://eevis.codes/blog/2023-07-17/android-developers-and-accessibility-challenges-and-proposed-solutions/#resources"> Resources</a></li>
</ol>
</nav>
<p>My current Big Project is my master's thesis - I'm writing about Android accessibility, and my goal is to create a list of checks Android developers could use to create more accessible apps. I'm super excited about the theme, and once it's ready, I will definitely share the results on my blog. </p>
<p>One of the main theoretical themes in the thesis is how Android developers implement accessibility and what challenges prevent them from doing so. Research also provides some solutions to these challenges. </p>
<p>In this blog post, we'll first look through some of the challenges Android developers face (although these are common for other types of development as well) and then discuss proposed solutions.</p>
<h2 id="the-challenges">The Challenges</h2>
<p>Let's first look at the challenges developers face. I've listed all the resources used in the <a href="https://eevis.codes/blog/2023-07-17/android-developers-and-accessibility-challenges-and-proposed-solutions/#resources">Resources section</a>. The list has three studies about Android developers, their accessibility knowledge, and their willingness and readiness to develop accessible apps.</p>
<h3 id="not-knowing-about-accessibility-and-lack-of-awareness">Not Knowing About Accessibility and Lack of Awareness</h3>
<p>The first problem I want to mention is that Android developers often lack accessibility knowledge. This lack of familiarity varies - for some, it's total unawareness, but for most, it shows as problems understanding the exact needs of disabled users. <sup><a href="https://eevis.codes/blog/2023-07-17/android-developers-and-accessibility-challenges-and-proposed-solutions/#1">1</a></sup></p>
<p>This finding is something I can confirm from my own experiences. Often Android developers are familiar with the fact that accessibility should be part of an app. Still, the exact implementation and why they must do something are unclear. </p>
<h3 id="companies-ignoring-accessibility-requirements">Companies Ignoring Accessibility Requirements</h3>
<p>Another aspect of the lack of awareness is that companies ignore the accessibility requirements. Patel et al. <sup><a href="https://eevis.codes/blog/2023-07-17/android-developers-and-accessibility-challenges-and-proposed-solutions/#2">2</a></sup> found out that the leadership in companies often prioritized other things over accessibility when a deadline was approaching. They usually see accessibility as an extra cost rather than an opportunity. This attitude is also visible in the internal policies, so for developers, it's often hard to find time to fix accessibility issues retroactively. </p>
<p>Another thing that is related to fixing accessibility as an afterthought is that it can be considered hard - and it might lead to a situation where those accessibility issues don't get fixed. <sup><a href="https://eevis.codes/blog/2023-07-17/android-developers-and-accessibility-challenges-and-proposed-solutions/#1">1</a></sup></p>
<p>I can again confirm these findings - throughout my career, I've witnessed situations where accessibility is not part of the company's requirements, which means there is no time or resources to consider accessibility.</p>
<h3 id="little-to-no-exposure-or-background-with-accessibility">Little to No Exposure or Background with Accessibility</h3>
<p>Vendome et al. <sup><a href="https://eevis.codes/blog/2023-07-17/android-developers-and-accessibility-challenges-and-proposed-solutions/#3">3</a></sup> found out that developers often don't have exposure or background with accessibility and assistive technology, and that leads to misunderstandings, e.g., in questions developers ask on platforms like Stack Overflow. For example, they might ask about how TalkBack reads phone numbers and how to force it to read them in a particular way. (For those unfamiliar with TalkBack: TalkBack users can control this with settings, and developers should not force it on apps.)</p>
<p>On the other hand, opportunities to interact with people with disabilities were considered as a helpful way to understand user expectations better. <sup><a href="https://eevis.codes/blog/2023-07-17/android-developers-and-accessibility-challenges-and-proposed-solutions/#2">2</a></sup> I think this all comes down to the fact that it's helpful to talk to users to understand their expectations and needs. It's a crucial part of creating good apps.</p>
<h3 id="accessibility-is-only-about-screen-reader-accessibility">Accessibility is Only About Screen Reader Accessibility</h3>
<p>One challenge for accessibility is that it's mostly about screen reader accessibility. Vendome et al. <sup><a href="https://eevis.codes/blog/2023-07-17/android-developers-and-accessibility-challenges-and-proposed-solutions/#3">3</a></sup> found out that the most discussed topic on Stack Overflow (in the material they collected) was screen reader accessibility. </p>
<p>I can second that finding from my experience - when discussing accessibility, it's often about screen reader accessibility. And it's an important aspect, but it's necessary to remember that it's not all. Considering interaction for people who can see or have low vision is as important as is, e.g., cognitive accessibility.</p>
<h3 id="belief-of-accessibility-having-negative-effects-on-the-apps-aesthetics-and-usability">Belief of Accessibility Having Negative Effects on the App's Aesthetics and Usability</h3>
<p>Another finding I've witnessed is that some developers (and designers) believe that if they incorporate accessibility into their apps or designs, it will affect the app's aesthetics and/or usability. Di Gregorio et al. <sup><a href="https://eevis.codes/blog/2023-07-17/android-developers-and-accessibility-challenges-and-proposed-solutions/#1">1</a></sup> found that developers might meet ideas about progressive functionality or interfaces negatively because they can add complexity to the app.</p>
<p>As a developer, I can understand where this comes from. Especially requirements added as an afterthought do increase complexity - so it's important to include accessibility right from the start, and building these interfaces is less work.</p>
<h3 id="lack-of-tools">Lack of Tools</h3>
<p>The second to last finding is the lack of tools for developing accessible apps. Also, some developers have had situations where free tools have disappeared suddenly, and there has been nothing to replace them. <sup><a href="https://eevis.codes/blog/2023-07-17/android-developers-and-accessibility-challenges-and-proposed-solutions/#2">2</a></sup> </p>
<p>Another thing related to different tools (or assistive technology) is that developers often don't have experience with those tools, which can lead to misunderstandings and solving problems that are not problems. One example is the problem described in the section "Little to No Exposure or Background with Accessibility" about TalkBack and numbers. TalkBack handles how numbers are presented, and users can alter that, but because developers didn't know it, they tried to force their app to represent numbers in a certain way when using TalkBack. <sup><a href="https://eevis.codes/blog/2023-07-17/android-developers-and-accessibility-challenges-and-proposed-solutions/#3">3</a></sup></p>
<p>From the perspective of a mobile dev with a background in web development, I agree that there aren't enough tools for developing accessible Android apps. There are way more tools for accessibility in web development, but for Android, not so much. The situation is changing, but slowly. I've also seen the lack of exposure to assistive technology and how it can lead to interesting outcomes.</p>
<h3 id="relevant-and-usable-information-is-hard-to-find">Relevant and Usable Information is Hard to Find</h3>
<p>The last finding is that it's hard to find relevant and usable information. One interviewee from Di Gregorio et al.'s research noted that it's not just about unawareness of accessibility guidelines but also about not being able to find usable information from the internet to fix the issues. <sup><a href="https://eevis.codes/blog/2023-07-17/android-developers-and-accessibility-challenges-and-proposed-solutions/#1">1</a></sup> Patel et al. <sup><a href="https://eevis.codes/blog/2023-07-17/android-developers-and-accessibility-challenges-and-proposed-solutions/#2">2</a></sup> also discovered a lack of resources - especially for building accessible components. And one of their study participants noted that the available accessibility guidelines should be in a form that is more digestible so that developers would use them.</p>
<p>I can fully agree. Especially with Android development, I often know what I should do regarding how the app should behave to be more accessible. Still, it's hard to find usable articles on <i>how</i> actually do the technical implementation. So I can only imagine how hard and frustrating it can feel for someone with less background in accessibility. </p>
<p>We've gone through many challenges Android developers face, and let's look next at the solutions proposed in the research. These solutions don't solve every aspect, but at least some of them. </p>
<h2 id="proposed-solutions">Proposed Solutions</h2>
<p>The three research articles I've looked at list two types of proposed solutions to solve the challenges of building accessible apps for Android: Better tools and education. Let's look at both of them more.</p>
<h3 id="better-tools-for-development">Better Tools for Development</h3>
<p>Both Patel et al. <sup><a href="https://eevis.codes/blog/2023-07-17/android-developers-and-accessibility-challenges-and-proposed-solutions/#2">2</a></sup> and Vendome et al. <sup><a href="https://eevis.codes/blog/2023-07-17/android-developers-and-accessibility-challenges-and-proposed-solutions/#3">3</a></sup> suggest having better tools for development to address accessibility in specific technical implementations. These tools could be integrated with, e.g., IDEs and could (or from what I've observed and talked with other developers, should) be semi-automated. </p>
<p>I've been following the Android development ecosystem for a while now, and there are ongoing processes to improve the tooling. There are some code checks in Android Studio for XML-based views, but they've been lacking from Compose components. However, in Google I/O, they announced that there would be improvements for Compose previews in the form of these accessibility warnings. (Source: <a href="https://goo.gle/IO23_AndroidAccess">What's new in Android Accessibility</a>)</p>
<h3 id="increase-accessibility-knowledge-through-formal-education">Increase Accessibility Knowledge Through (Formal) Education</h3>
<p>Another proposed solution is to increase accessibility knowledge through education. That could be achieved by adding more accessibility-related topics to curriculums of non-computing fields. Also, within computer science and related fields, learning about addressing accessibility issues during development would be beneficial. <sup><a href="https://eevis.codes/blog/2023-07-17/android-developers-and-accessibility-challenges-and-proposed-solutions/#2">2</a></sup></p>
<p>This solution goes past formal education - developers would generally benefit from learning more about universal design principles and targeted tutorials and workshops addressing specific accessibility topics. <sup><a href="https://eevis.codes/blog/2023-07-17/android-developers-and-accessibility-challenges-and-proposed-solutions/#2">2</a></sup> <sup><a href="https://eevis.codes/blog/2023-07-17/android-developers-and-accessibility-challenges-and-proposed-solutions/#3">3</a></sup></p>
<p>From my perspective, on both formal and informal education, I've been happy to notice the increase of accessibility-themed articles and workshops, as well as accessibility in the curriculums of, e.g., universities. There is, of course, a lot to be done, but the direction is correct.</p>
<p>But even if we had many quality articles and other resources, it's always each developer's responsibility to learn more about the topic. Any amount of education or materials is only enough if we, as developers, take the opportunity to learn more. </p>
<h2 id="wrapping-up">Wrapping Up</h2>
<p>In this blog post, I've gone through some challenges Android developers face regarding accessibility per prior research. I've discussed lack of awareness, companies ignoring accessibility requirements, developers having little to no exposure or background with accessibility, accessibility being only about screen reader accessibility, the belief that accessibility makes the app less usable or beautiful, lack of tools, and that relevant and usable information is hard to find. </p>
<p> I've also shared some suggested solutions: better tooling for development and increasing accessibility knowledge through formal and informal education. </p>
<p>Do you recognize these challenges in your work? Or do you have additional solution ideas? </p>
<h2 id="resources">Resources</h2>
<ul>
<li id="1"><sup>1</sup> Di Gregorio, M., Di Nucci, D., Palomba, F., & Vitiello, G. (2022). The making of accessible Android applications: An empirical study on the state of the practice. Empirical Software Engineering, 27(6), 145. <a href="https://doi.org/10.1007/s10664-022-10182-x">https://doi.org/10.1007/s10664-022-10182-x</a></li>
<li id="2"><sup>2</sup> Patel, R., Breton, P., Baker, C. M., El-Glaly, Y. N., & Shinohara, K. (2020). Why Software is Not Accessible: Technology Professionals' Perspectives and Challenges. Extended Abstracts of the 2020 CHI Conference on Human Factors in Computing Systems, 1–9. <a href="https://doi.org/10.1145/3334480.3383103">https://doi.org/10.1145/3334480.3383103</a></li>
<li id="3"><sup>3</sup> Vendome, C., Solano, D., Liñán, S., & Linares-Vásquez, M. (2019). Can Everyone use my app? An Empirical Study on Accessibility in Android Apps. 2019 IEEE International Conference on Software Maintenance and Evolution (ICSME), 41–52. <a href="https://doi.org/10.1109/ICSME.2019.00014">https://doi.org/10.1109/ICSME.2019.00014</a></li>
</ul>
</article>
More Accessible Graphs with Jetpack Compose Part 1: Adding Content Description2023-07-24T00:00:00.000Zhttps://eevis.codes/blog/2023-07-24/more-accessible-graphs-with-jetpack-compose-part-1-adding-content/
<nav id="breadcrumbs" aria-label="breadcrumbs">
<ol>
<li>
<a href="https://eevis.codes/blog/">Blog</a>
<span aria-hidden="true" class="breadcrumb-separator">/<span>
</span></span></li>
<li>More Accessible Graphs with Jetpack Compose Part 1: Adding Content Description</li><li>
</li></ol>
</nav>
<article class="blogPostLong">
<h1 id="/2023-07-24/more-accessible-graphs-with-jetpack-compose-part-1-adding-content">More Accessible Graphs with Jetpack Compose Part 1: Adding Content Description</h1>
<div class="reading-time-and-date">
<p>
Reading time: about 7 minutes
</p>
<span class="separator"></span>
<p>Published <time datetime="24th Jul, 2023">24th Jul, 2023</time></p>
</div>
<section class="likes-and-boosts">
<div>
<svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="boosts-title">
<title id="boosts-title">Boosts</title>
<path d="M7.19 4.35047C5.15 6.64047 3.75 9.93047 3.62 10.2405L0 8.69047L4.05 4.64047C4.52 4.17047 5.2 3.96047 5.86 4.09047L7.19 4.35047ZM9.17 15.0005C9.17 15.0005 12.91 13.4505 15.06 11.3005C20.46 5.90047 19.56 1.68047 19.27 0.730473C18.32 0.430473 14.1 -0.459527 8.7 4.94047C6.55 7.09047 5 10.8305 5 10.8305L9.17 15.0005ZM15.65 12.8105C13.36 14.8505 10.07 16.2505 9.76 16.3805L11.31 20.0005L15.36 15.9505C15.83 15.4805 16.04 14.8005 15.91 14.1405L15.65 12.8105ZM7 16.0005C7 16.8305 6.66 17.5805 6.12 18.1205C4.94 19.3005 0 20.0005 0 20.0005C0 20.0005 0.7 15.0605 1.88 13.8805C2.2988 13.4595 2.83325 13.1724 3.4155 13.0558C3.99775 12.9392 4.60153 12.9982 5.15017 13.2253C5.69882 13.4525 6.16758 13.8376 6.49694 14.3317C6.8263 14.8258 7.0014 15.4067 7 16.0005ZM11 7.00047C11 5.90047 11.9 5.00047 13 5.00047C14.1 5.00047 15 5.90047 15 7.00047C15 8.10047 14.1 9.00047 13 9.00047C11.9 9.00047 11 8.10047 11 7.00047Z" fill="#00696D"></path>
</svg>
0
</div>
<div>
<svg role="img" width="20" height="19" viewBox="0 0 20 19" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="likes-title">
<title id="likes-title">Likes</title>
<path d="M10 18.35L8.55 17.03C3.4 12.36 0 9.27 0 5.5C0 2.41 2.42 0 5.5 0C7.24 0 8.91 0.81 10 2.08C11.09 0.81 12.76 0 14.5 0C17.58 0 20 2.41 20 5.5C20 9.27 16.6 12.36 11.45 17.03L10 18.35Z"></path>
</svg>
0
</div>
</section>
<img class="cover-photo" src="https://images.ctfassets.net/mpqufjsy02zr/6tWE5hbm2m2olZnHb8VfNA/dec6d96861a8f1c12ac887911edd4ee3/more-accessible-graphs-1.png" alt="More Accessible Graphs with Jetpack Compose Part 1: Adding Content Description" />
<div class="blog-tags">
<a href="https://eevis.codes/tags/a11y"><span class="blog-tag turquoise">a11y</span></a> <a href="https://eevis.codes/tags/Android"><span class="blog-tag blue">Android</span></a>
</div>
<nav aria-label="More Accessible Graphs with Jetpack Compose Part 1: Adding Content Description Table of Contents" class="table-of-contents">
<h2>Table of Contents</h2>
<ol>
<li><a href="https://eevis.codes/blog/2023-07-24/more-accessible-graphs-with-jetpack-compose-part-1-adding-content/#the-initial-project-code"> The Initial Project Code</a></li><li><a href="https://eevis.codes/blog/2023-07-24/more-accessible-graphs-with-jetpack-compose-part-1-adding-content/#adding-content-descriptions-to-items-on-graph"> Adding Content Descriptions to Items on Graph</a></li><li><a href="https://eevis.codes/blog/2023-07-24/more-accessible-graphs-with-jetpack-compose-part-1-adding-content/#wrapping-up"> Wrapping up</a></li><li><a href="https://eevis.codes/blog/2023-07-24/more-accessible-graphs-with-jetpack-compose-part-1-adding-content/#links-in-the-blog-post"> Links in the Blog Post</a></li>
</ol>
</nav>
<p>Data visualizations rely on the visual representation of data. And that data is usually portrayed by some combination of colors. On mobile apps, they also often include different interactions that rely on touch. </p>
<p>But what if your user can't see? Or if they're colorblind, and the combination of colors is not visible to them? Or if they use a switch device or hardware keyboard for navigation?</p>
<p>From my experience, data visualizations of apps are often inaccessible for these groups of users, for instance. I wanted to experiment with how much changes and code would be needed to make a line chart more accessible. To my surprise, the amount of code wasn't that much. Of course, experimenting took some time, but after solving these problems, I now have an example project I can utilize in other projects.</p>
<p>And because I love to share what I've learned, I'm writing this series of blog posts to help you to build more accessible graphs with Jetpack Compose. We'll look at three different aspects:</p>
<ul>
<li>Adding information for non-visual access users (so, e.g., a TalkBack user)</li>
<li>Adding keyboard interaction in addition to touch-based interaction</li>
<li>Differentiating data by other means than just color</li>
</ul>
<p>This first blog post in the series is about adding information for non-visual access users, e.g., screen reader users. The following two blog posts will cover adding keyboard interaction and differentiating data with other means than just color. There might be additional posts about voice access and increasing touch target size in the future.</p>
<p>My goal is not to provide a final solution but ideas you can take and improve to use in your codebase. Of course, graphs can be more complex, and these relatively simple solutions might not work for everything, but I hope they'll give you pointers on improving your graph's accessibility.</p>
<p>Let's first talk about the example project I prepared for this experiment.</p>
<h2 id="the-initial-project-code">The Initial Project Code</h2>
<p>So, before starting any explorations on making things more accessible, I needed to build a small example app. You can find all the code in this blog post from the <a href="https://github.com/eevajonnapanula/graph-accessibility-example/">Graph Example-project repository</a>. The <code>main</code>-branch contains the final version with all the changes, and the <a href="https://github.com/eevajonnapanula/graph-accessibility-example/tree/starting-point"><code>starting-point</code>-branch</a> has the initial code I started tweaking.</p>
<p>Here's a short video on what I built:</p>
<video controls="" class="portrait-video">
<source src="https://videos.ctfassets.net/mpqufjsy02zr/ZHIMzyIPuomkiPmCrlxYs/573cef254ce48f72e302d972a103a8a7/final-starting-point.mp4" type="video/mp4" />
</video>
<p>The app has a line graph containing data from woman applicants in Finnish higher education per year (starting from 2015) from two fields: Information Communication Technology and Engineering (non-ICT). The graph shows the percentage of women applicants for these fields individually and displays the total percentages for these two fields.</p>
<p>When a user touches and horizontally drags a pointer over the graph, the selected year's percentages are shown in the bottom right corner. These values are not available in any other way - so if a user can't use a pointer, they would miss this information. </p>
<p>Technically, this graph is built with the Canvas-API, which adds some restrictions on how to add, e.g., content description to elements. And that's something we want to do - as that is one way to communicate the values to someone who can't see the texts. Let's next look at how we can add them to the graph.</p>
<h2 id="adding-content-descriptions-to-items-on-graph">Adding Content Descriptions to Items on Graph</h2>
<p>Because of how the graph is utilizing Canvas-API, it means that it's completely hidden from accessibility services. In practice, it means that someone using, e.g., TalkBack can't access the values inside the graph.
Also, because the labels on x- and y-axes are built with the <code>drawText</code>-method, they're unavailable. Here's an example of how TalkBack reads through the screen:</p>
<video controls="" class="portrait-video">
<source src="https://videos.ctfassets.net/mpqufjsy02zr/2HreTsGzqBzKthDIcFsO7E/d239c0a550fb294e5622bb67e28f4535/Screen_Recording_20230723_071526_GraphExample.mp4" type="video/mp4" />
</video>
<p>On the video, I'm navigating through the screen with TalkBack. The video shows that the cursor entirely skips the graph. It reads the title before and the labels under the graph but nothing from it. Let's start improving the experience by adding a <code>Highlighter</code>-component.</p>
<h3 id="add-highlighter-component">Add <code>Highlighter</code>-Component</h3>
<p>In this case, the <code>Highlighter</code>-component is an overlay over the graph, highlighting the selected section, and should be visible only when focused. Technically, it's a <code>Box</code> that overlays the whole graph, with smaller <code>Box</code>-elements that are the size of the highlighted area. This component also helps improve the keyboard and switch interaction, as we'll see in the later blog post.</p>
<p>The <code>Highlighter</code>-component looks like this: </p>
<pre><code class="language-kotlin"><span class="token annotation builtin">@Composable</span>
<span class="token keyword">fun</span> <span class="token function">Highlighter</span><span class="token punctuation">(</span>
modifier<span class="token operator">:</span> Modifier <span class="token operator">=</span> Modifier<span class="token punctuation">,</span>
widthBetweenPoints<span class="token operator">:</span> Float<span class="token punctuation">,</span>
pixelPointsForTotal<span class="token operator">:</span> List<span class="token operator"><</span>Point<span class="token operator">></span><span class="token punctuation">,</span>
pixelPointsForTech<span class="token operator">:</span> List<span class="token operator"><</span>Point<span class="token operator">></span><span class="token punctuation">,</span>
pixelPointsForIct<span class="token operator">:</span> List<span class="token operator"><</span>Point<span class="token operator">></span><span class="token punctuation">,</span>
highlightedX<span class="token operator">:</span> Float<span class="token operator">?</span>
<span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token function">Box</span><span class="token punctuation">(</span>
modifier
<span class="token punctuation">.</span><span class="token function">fillMaxSize</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
<span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">val</span> sectionWidth <span class="token operator">=</span> <span class="token function">with</span><span class="token punctuation">(</span>LocalDensity<span class="token punctuation">.</span>current<span class="token punctuation">)</span> <span class="token punctuation">{</span>
widthBetweenPoints<span class="token punctuation">.</span><span class="token function">toDp</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
pixelPointsForTotal<span class="token punctuation">.</span><span class="token function">forEachIndexed</span> <span class="token punctuation">{</span> index<span class="token punctuation">,</span> point <span class="token operator">-></span>
<span class="token keyword">val</span> xOffset <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">(</span>index <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">)</span> <span class="token operator">*</span> widthBetweenPoints <span class="token operator">-</span> widthBetweenPoints <span class="token operator">*</span> <span class="token number">0.66f</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toInt</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token keyword">var</span> isHighlighted <span class="token keyword">by</span> remember <span class="token punctuation">{</span> <span class="token function">mutableStateOf</span><span class="token punctuation">(</span><span class="token boolean">false</span><span class="token punctuation">)</span> <span class="token punctuation">}</span>
<span class="token keyword">var</span> position <span class="token keyword">by</span> remember <span class="token punctuation">{</span> <span class="token function">mutableStateOf</span><span class="token punctuation">(</span><span class="token function">Pair</span><span class="token punctuation">(</span><span class="token number">0f</span><span class="token punctuation">,</span> <span class="token number">0f</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">}</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>highlightedX <span class="token operator">==</span> <span class="token keyword">null</span><span class="token punctuation">)</span> isHighlighted <span class="token operator">=</span> <span class="token boolean">false</span>
highlightedX<span class="token operator">?</span><span class="token punctuation">.</span><span class="token function">let</span> <span class="token punctuation">{</span>
isHighlighted <span class="token operator">=</span> it <span class="token operator">></span> <span class="token punctuation">(</span>position<span class="token punctuation">.</span>first <span class="token operator">-</span> widthBetweenPoints<span class="token punctuation">)</span> <span class="token operator">&&</span> it <span class="token operator"><</span> <span class="token punctuation">(</span>position<span class="token punctuation">.</span>second <span class="token operator">-</span> widthBetweenPoints<span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token function">Box</span><span class="token punctuation">(</span>
modifier <span class="token operator">=</span> Modifier
<span class="token punctuation">.</span><span class="token function">fillMaxHeight</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">width</span><span class="token punctuation">(</span>sectionWidth<span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">offset</span> <span class="token punctuation">{</span> <span class="token function">IntOffset</span><span class="token punctuation">(</span>xOffset<span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">}</span>
<span class="token punctuation">.</span><span class="token function">border</span><span class="token punctuation">(</span>
width <span class="token operator">=</span> Graph<span class="token punctuation">.</span>Highlighter<span class="token punctuation">.</span>width<span class="token punctuation">,</span>
color <span class="token operator">=</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>isHighlighted<span class="token punctuation">)</span> MaterialTheme<span class="token punctuation">.</span>colorScheme<span class="token punctuation">.</span>onBackground <span class="token keyword">else</span> Color<span class="token punctuation">.</span>Transparent<span class="token punctuation">,</span>
shape <span class="token operator">=</span> <span class="token function">RoundedCornerShape</span><span class="token punctuation">(</span>Graph<span class="token punctuation">.</span>Highlighter<span class="token punctuation">.</span>borderRadius<span class="token punctuation">)</span><span class="token punctuation">,</span>
<span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">onGloballyPositioned</span> <span class="token punctuation">{</span>
position <span class="token operator">=</span>
<span class="token function">Pair</span><span class="token punctuation">(</span>
it<span class="token punctuation">.</span><span class="token function">positionInParent</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span>x<span class="token punctuation">,</span>
it<span class="token punctuation">.</span><span class="token function">positionInParent</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span>x <span class="token operator">+</span> it<span class="token punctuation">.</span>size<span class="token punctuation">.</span>width
<span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>It takes the following parameters:</p>
<ul>
<li>modifier: Modifier - a modifier to pass down styles. Defaults to <code>Modifier</code></li>
<li>widthBetweenPoints: Float - as the name states, width between the points. It's used to position the highlighting section correctly </li>
<li>pixelPointsForTotal: List<Point> - List of values for total as <code>Point</code></Point></li>
<li>pixelPointsForTech: List<Point> - List of values for engineering as <code>Point</code> (should be renamed to <code>pixelPointsForEng</code>)</Point></li>
<li>pixelPointsForIct: List<Point> - List of values for ICT-field as <code>Point</code></Point></li>
<li>highlightedX: Float? - x-value of the currently highlighted item</li>
</ul>
<aside class="info">
<p>Note: The <code>Point</code> is a data class defined in the code, and the definition looks like this:</p>
<pre>
<code class="language-kotlin">
data class Point(
val x: Float,
val y: Float,
val year: Int,
val percentage: Float,
val isHighlighted: Boolean = false,
) {
val percentageString = "${percentage.toInt()} %"
}
</code>
</pre>
</aside>
<p>Inside the component, all the pixel point values for the total (but this could be any of the lists) are mapped, and the lambda returns a highlighting section for each point. The offset for each section is calculated with the help of <code>widthBetweenPoints.</code> </p>
<p>We'll also need to get the position of the highlighter. The values are the start and end x-coordinates of the component. We'll save it to state, and on the <code>Box</code>-component, we change the value on the <code>onGloballyPositioned</code>-modifier:</p>
<pre><code class="language-kotlin"><span class="token keyword">var</span> position <span class="token keyword">by</span> remember <span class="token punctuation">{</span> <span class="token function">mutableStateOf</span><span class="token punctuation">(</span><span class="token function">Pair</span><span class="token punctuation">(</span><span class="token number">0f</span><span class="token punctuation">,</span> <span class="token number">0f</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">}</span>
<span class="token operator">..</span><span class="token punctuation">.</span>
<span class="token punctuation">.</span><span class="token function">onGloballyPositioned</span> <span class="token punctuation">{</span>
position <span class="token operator">=</span>
<span class="token function">Pair</span><span class="token punctuation">(</span>
it<span class="token punctuation">.</span><span class="token function">positionInParent</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span>x<span class="token punctuation">,</span>
it<span class="token punctuation">.</span><span class="token function">positionInParent</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span>x <span class="token operator">+</span> it<span class="token punctuation">.</span>size<span class="token punctuation">.</span>width
<span class="token punctuation">)</span>
<span class="token punctuation">}</span>
</code></pre><p>We can then use these values to determine if the currently selected year (the <code>highlightedX</code>-parameter) is inside the area of this highlighter component.</p>
<pre><code class="language-kotlin"><span class="token keyword">var</span> isHighlighted <span class="token keyword">by</span> remember <span class="token punctuation">{</span> <span class="token function">mutableStateOf</span><span class="token punctuation">(</span><span class="token boolean">false</span><span class="token punctuation">)</span> <span class="token punctuation">}</span>
<span class="token operator">..</span><span class="token punctuation">.</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>highlightedX <span class="token operator">==</span> <span class="token keyword">null</span><span class="token punctuation">)</span> isHighlighted <span class="token operator">=</span> <span class="token boolean">false</span>
highlightedX<span class="token operator">?</span><span class="token punctuation">.</span><span class="token function">let</span> <span class="token punctuation">{</span>
isHighlighted <span class="token operator">=</span>
it <span class="token operator">></span> <span class="token punctuation">(</span>position<span class="token punctuation">.</span>first <span class="token operator">-</span> widthBetweenPoints<span class="token punctuation">)</span>
<span class="token operator">&&</span> it <span class="token operator"><</span> <span class="token punctuation">(</span>position<span class="token punctuation">.</span>second <span class="token operator">-</span> widthBetweenPoints<span class="token punctuation">)</span>
<span class="token punctuation">}</span>
</code></pre><p>In the code snippet, <code>isHighlighted</code> stores the value of whether the pointer is inside its area. If <code>highlightedX</code> is null (there's no pointer input on the graph), then <code>isHighlighted</code> is false. </p>
<p>We can then use<code>isHighlighted</code> to change the border color of the area in the <code>border</code>-modifier:</p>
<pre><code class="language-kotlin"><span class="token punctuation">.</span><span class="token function">border</span><span class="token punctuation">(</span>
width <span class="token operator">=</span> Graph<span class="token punctuation">.</span>Highlighter<span class="token punctuation">.</span>width<span class="token punctuation">,</span>
color <span class="token operator">=</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>isHighlighted<span class="token punctuation">)</span> MaterialTheme<span class="token punctuation">.</span>colorScheme<span class="token punctuation">.</span>onBackground <span class="token keyword">else</span> Color<span class="token punctuation">.</span>Transparent<span class="token punctuation">,</span>
shape <span class="token operator">=</span> <span class="token function">RoundedCornerShape</span><span class="token punctuation">(</span>Graph<span class="token punctuation">.</span>Highlighter<span class="token punctuation">.</span>borderRadius<span class="token punctuation">)</span><span class="token punctuation">,</span>
<span class="token punctuation">)</span>
</code></pre><p>This image shows the current state when the year 2019 is highlighted:</p>
<img src="https://images.ctfassets.net/mpqufjsy02zr/3eI40qnknkwMZePlBmQrTJ/a46b6e55a27fe4b3a9229873eb3117d9/Screenshot_20230723_072659.png" alt="Year 2019 from the graph has a white rectangle around it, and the points for each graph are also rectangles instead of circles. At the right bottom of the graph values are visible: 2019: All: 27%, Eng.: 26% and ICT: 28%." class="portrait-img" />
<p>We now have a highlighter component. The current implementation just outlines the current selection when using pointer input, so it's not improving accessibility that much just yet. Let's next add some content descriptions to make the graph more accessible.</p>
<h3 id="add-content-description">Add Content Description</h3>
<p>Next, we want to add a content description for anyone using assistive technology such as TalkBack. We first want to add the <code>focusable</code>- modifier to each highlighter's child element. As the name states, it makes the element focusable, meaning people with different assistive technologies can reach it. Without it, the element is hidden from a screen reader and not focusable with, e.g., a hardware keyboard or switch device. </p>
<p>The second step is to add the content description for each highlighter's child element. Because we use the <code>Box</code>-component, we need to use the <code>semantics</code>-modifier's <code>contentDescription</code>-property - contrary to the <code>contentDescription</code>-property available for some components (such as <code>Image</code>s). </p>
<p>As each highlighted section displays the year and values for ICT, engineering, and total, that's what we want to add to the content description as it is the relevant information for that section. We first form the content description inside <code>pixelPointsForTotal.forEachIndexed {}</code>:</p>
<pre><code class="language-kotlin"><span class="token keyword">val</span> contentDesc <span class="token operator">=</span>
<span class="token string-literal singleline"><span class="token string">"</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span><span class="token expression">point<span class="token punctuation">.</span>year</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">: "</span></span> <span class="token operator">+</span>
<span class="token string-literal singleline"><span class="token string">"</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span><span class="token expression"><span class="token function">stringResource</span><span class="token punctuation">(</span>id <span class="token operator">=</span> R<span class="token punctuation">.</span>string<span class="token punctuation">.</span>all<span class="token punctuation">)</span></span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token string"> </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span><span class="token expression">point<span class="token punctuation">.</span>percentageString</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">, "</span></span> <span class="token operator">+</span>
<span class="token string-literal singleline"><span class="token string">"</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span><span class="token expression"><span class="token function">stringResource</span><span class="token punctuation">(</span>id <span class="token operator">=</span> R<span class="token punctuation">.</span>string<span class="token punctuation">.</span>eng<span class="token punctuation">)</span></span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token string"> </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span><span class="token expression">pixelPointsForTech<span class="token punctuation">[</span>index<span class="token punctuation">]</span><span class="token punctuation">.</span>percentageString</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">, "</span></span> <span class="token operator">+</span>
<span class="token string-literal singleline"><span class="token string">"</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span><span class="token expression"><span class="token function">stringResource</span><span class="token punctuation">(</span>id <span class="token operator">=</span> R<span class="token punctuation">.</span>string<span class="token punctuation">.</span>ict<span class="token punctuation">)</span></span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token string"> </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span><span class="token expression">pixelPointsForIct<span class="token punctuation">[</span>index<span class="token punctuation">]</span><span class="token punctuation">.</span>percentageString</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">"</span></span>
</code></pre><p>Then we add the <code>focusable</code> modifier and use the content description we defined in the <code>semantics</code>-modifier:</p>
<pre><code class="language-kotlin"><span class="token operator">..</span><span class="token punctuation">.</span>
<span class="token punctuation">.</span><span class="token function">focusable</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">semantics</span> <span class="token punctuation">{</span>
contentDescription <span class="token operator">=</span> contentDesc
<span class="token punctuation">}</span><span class="token punctuation">,</span>
</code></pre><p>This way, when a user focuses on a highlighted section with a screen reader, they would hear (or get in Braille) the following:</p>
<video controls="" class="portrait-video">
<source src="https://videos.ctfassets.net/mpqufjsy02zr/5pAdMf1raWtYA8gY5kkHxT/38abd7cf519363fcd4a4cec087968927/Screen_Recording_20230723_072037_GraphExample.mp4" />
</video>
<p>The <a href="https://github.com/eevajonnapanula/graph-accessibility-example/commit/42aba1bdd4cc15ccd21881a61b9ce46185c86a73">complete difference in code for this section is available in this PR</a>.</p>
<h2 id="wrapping-up">Wrapping up</h2>
<p>In this blog post, we've looked at the initial code of the Graph Example project and how it's built. We then added a highlighter component to help us with our task of creating more accessible graphs. Finally, we added a content description for each highlighted section.</p>
<p>Please remember that this code is simplified, and when applying it to your codebase, you'll probably need to find out how to tweak it to work correctly. </p>
<p>Do you have any questions, comments, or feedback, or want to say anything else? Please share; I'd like to hear it!</p>
<h2 id="links-in-the-blog-post">Links in the Blog Post</h2>
<ul>
<li><a href="https://github.com/eevajonnapanula/graph-accessibility-example/">Graph Example-project repository</a></li>
<li><a href="https://github.com/eevajonnapanula/graph-accessibility-example/tree/starting-point"><code>starting-point</code>-branch</a> </li>
<li><a href="https://github.com/eevajonnapanula/graph-accessibility-example/commit/42aba1bdd4cc15ccd21881a61b9ce46185c86a73">Complete difference in code for this section is available in this PR</a></li>
</ul>
</article>
More Accessible Graphs with Jetpack Compose Part 2: Adding Keyboard Interaction2023-07-31T00:00:00.000Zhttps://eevis.codes/blog/2023-07-31/more-accessible-graphs-with-jetpack-compose-part-2-adding-keyboard-interaction/
<nav id="breadcrumbs" aria-label="breadcrumbs">
<ol>
<li>
<a href="https://eevis.codes/blog/">Blog</a>
<span aria-hidden="true" class="breadcrumb-separator">/<span>
</span></span></li>
<li>More Accessible Graphs with Jetpack Compose Part 2: Adding Keyboard Interaction</li><li>
</li></ol>
</nav>
<article class="blogPostLong">
<h1 id="/2023-07-31/more-accessible-graphs-with-jetpack-compose-part-2-adding-keyboard-interaction">More Accessible Graphs with Jetpack Compose Part 2: Adding Keyboard Interaction</h1>
<div class="reading-time-and-date">
<p>
Reading time: about 4 minutes
</p>
<span class="separator"></span>
<p>Published <time datetime="31st Jul, 2023">31st Jul, 2023</time></p>
</div>
<section class="likes-and-boosts">
<div>
<svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="boosts-title">
<title id="boosts-title">Boosts</title>
<path d="M7.19 4.35047C5.15 6.64047 3.75 9.93047 3.62 10.2405L0 8.69047L4.05 4.64047C4.52 4.17047 5.2 3.96047 5.86 4.09047L7.19 4.35047ZM9.17 15.0005C9.17 15.0005 12.91 13.4505 15.06 11.3005C20.46 5.90047 19.56 1.68047 19.27 0.730473C18.32 0.430473 14.1 -0.459527 8.7 4.94047C6.55 7.09047 5 10.8305 5 10.8305L9.17 15.0005ZM15.65 12.8105C13.36 14.8505 10.07 16.2505 9.76 16.3805L11.31 20.0005L15.36 15.9505C15.83 15.4805 16.04 14.8005 15.91 14.1405L15.65 12.8105ZM7 16.0005C7 16.8305 6.66 17.5805 6.12 18.1205C4.94 19.3005 0 20.0005 0 20.0005C0 20.0005 0.7 15.0605 1.88 13.8805C2.2988 13.4595 2.83325 13.1724 3.4155 13.0558C3.99775 12.9392 4.60153 12.9982 5.15017 13.2253C5.69882 13.4525 6.16758 13.8376 6.49694 14.3317C6.8263 14.8258 7.0014 15.4067 7 16.0005ZM11 7.00047C11 5.90047 11.9 5.00047 13 5.00047C14.1 5.00047 15 5.90047 15 7.00047C15 8.10047 14.1 9.00047 13 9.00047C11.9 9.00047 11 8.10047 11 7.00047Z" fill="#00696D"></path>
</svg>
0
</div>
<div>
<svg role="img" width="20" height="19" viewBox="0 0 20 19" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="likes-title">
<title id="likes-title">Likes</title>
<path d="M10 18.35L8.55 17.03C3.4 12.36 0 9.27 0 5.5C0 2.41 2.42 0 5.5 0C7.24 0 8.91 0.81 10 2.08C11.09 0.81 12.76 0 14.5 0C17.58 0 20 2.41 20 5.5C20 9.27 16.6 12.36 11.45 17.03L10 18.35Z"></path>
</svg>
0
</div>
</section>
<img class="cover-photo" src="https://images.ctfassets.net/mpqufjsy02zr/6NRAksRlj6aU1J2vsoydVR/1e29303844480df742b3482389a38950/more-accessible-graphs-2.png" alt="More Accessible Graphs with Jetpack Compose Part 2: Adding Keyboard Interaction" />
<div class="blog-tags">
<a href="https://eevis.codes/tags/a11y"><span class="blog-tag turquoise">a11y</span></a> <a href="https://eevis.codes/tags/Android"><span class="blog-tag blue">Android</span></a>
</div>
<nav aria-label="More Accessible Graphs with Jetpack Compose Part 2: Adding Keyboard Interaction Table of Contents" class="table-of-contents">
<h2>Table of Contents</h2>
<ol>
<li><a href="https://eevis.codes/blog/2023-07-31/more-accessible-graphs-with-jetpack-compose-part-2-adding-keyboard-interaction/#adding-keyboard-interaction"> Adding Keyboard Interaction</a></li><li><a href="https://eevis.codes/blog/2023-07-31/more-accessible-graphs-with-jetpack-compose-part-2-adding-keyboard-interaction/#wrapping-up"> Wrapping Up</a></li><li><a href="https://eevis.codes/blog/2023-07-31/more-accessible-graphs-with-jetpack-compose-part-2-adding-keyboard-interaction/#links-in-the-blog-post"> Links in the Blog Post</a></li>
</ol>
</nav>
<p>Welcome to the second episode of "More Accessible Graphs with Jetpack Compose" - in this blog post, we'll continue from where we left off in the first one. We will look at improving the (physical) keyboard access for graphs in Jetpack Compose. Link to the first part: <a href="https://eevis.codes/blog/2023-07-24/more-accessible-graphs-with-jetpack-compose-part-1-adding-content/">More Accessible Graphs with Jetpack Compose Part 1: Adding Content Description</a></p>
<p>At the end of the blog post, I mentioned that the second part would be about keyboard and switch access. After testing my code for the blog post, I realized that the switch access part didn't work. After some deep-diving into documentation, I admitted I wouldn't add it to this post as it would require a lot of research. So there likely will be at least four parts in this "More Accessible Graphs with Jetpack Compose"-blog post series.</p>
<p>In the first blog post, we added a <code>Higlighter</code>-component, an overlay on top of the line graph I had built. Then we made it focusable and added a content description for accessibility services. </p>
<p>After these changes, a keyboard user can already focus on the different parts of the graph, but there's one problem: The graph legend is not visible. And you may now wonder if it is really a problem - we added the content description, right?</p>
<p>The content description is unavailable if you only use a keyboard. If a person, who can see and uses a hardware device for navigation, navigates with the graph in the current state, they probably get frustrated because this is what they would get:</p>
<video controls="" class="portrait-video">
<source src="https://videos.ctfassets.net/mpqufjsy02zr/7jdl1GxtBF2FhxvGpQXJp1/395ee860ee3f62c52b14e742f2296d29/keyboard-start-en.mp4" type="video/mp4" />
</video>
<p>They can see that the focus changes but can't see the individual values. If I encountered something like this, I would think there is a bug in the app. </p>
<h2 id="adding-keyboard-interaction">Adding Keyboard Interaction</h2>
<p>Users can already focus on each section, so we no longer need to worry about that. But we'll need a way to change the currently focused position on the x-axis - like if a user was moving their finger on the graph. </p>
<p>The <code>highlightedX</code>-parameter is the one that controls the currently highlighted position on the x-axis. We'll need to add a way to change it programmatically when a highlighter section gets focus. Let's first add a function to do that: </p>
<pre><code class="language-kotlin"><span class="token annotation builtin">@Composable</span>
<span class="token keyword">fun</span> <span class="token function">Highlighter</span><span class="token punctuation">(</span>
<span class="token operator">..</span><span class="token punctuation">.</span>
setFocus<span class="token operator">:</span> <span class="token punctuation">(</span>Float<span class="token punctuation">)</span> <span class="token operator">-></span> Unit
<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token operator">..</span><span class="token punctuation">.</span> <span class="token punctuation">}</span>
</code></pre><p>On the parent component, which stores <code>highlightedX,</code> we define a function to change the value of highlighted X-position:</p>
<pre><code class="language-kotlin"><span class="token function">Highlighter</span><span class="token punctuation">(</span>
<span class="token operator">..</span><span class="token punctuation">.</span>
setFocus <span class="token operator">=</span> <span class="token punctuation">{</span> newX <span class="token operator">-></span>
highlightedX <span class="token operator">=</span> newX
<span class="token punctuation">}</span>
<span class="token punctuation">)</span>
</code></pre><p>Okay, now we have a function to control the x-position and switch the currently focused position on the x-axis. We want to pass the x-position of the currently focused highlighter section to the function, and for that, we attach an <code>onFocusChanged</code>-modifier.</p>
<pre><code class="language-kotlin"><span class="token punctuation">.</span><span class="token function">onFocusChanged</span> <span class="token punctuation">{</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>it<span class="token punctuation">.</span>isFocused<span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token function">setFocus</span><span class="token punctuation">(</span>point<span class="token punctuation">.</span>x<span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>Within the modifier, we check if the current section is focused, and if it is, we use the <code>setFocus</code>-function, passing the x-position of that currently focused section. </p>
<p>After this change, we already have working code - focusing on a highlighter section that shows the graph label so that a user can see the values for each point on that focused year.</p>
<p>There is just a minor tweak we still want to do. Right now, the border of the highlighter is visible if there is a focus on the component from both hardware devices and pointer input. We don't want to show the highlighter component's border for pointer input because there is already a different highlighter (a dashed line) when a user uses pointer input.</p>
<p>We will fix that by adding a state for border color, setting it to transparent by default, and then updating it when the focus changes. </p>
<pre><code class="language-kotlin"><span class="token keyword">var</span> color <span class="token keyword">by</span> remember <span class="token punctuation">{</span> <span class="token function">mutableStateOf</span><span class="token punctuation">(</span>Color<span class="token punctuation">.</span>Transparent<span class="token punctuation">)</span> <span class="token punctuation">}</span>
<span class="token operator">..</span><span class="token punctuation">.</span>
<span class="token comment">// Then on .onFocusChanged-modifier:</span>
<span class="token punctuation">.</span><span class="token function">onFocusChanged</span> <span class="token punctuation">{</span>
color <span class="token operator">=</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>it<span class="token punctuation">.</span>isFocused<span class="token punctuation">)</span> focusedColor <span class="token keyword">else</span> Color<span class="token punctuation">.</span>Transparent
<span class="token operator">..</span><span class="token punctuation">.</span>
<span class="token punctuation">}</span>
</code></pre><p>And we use that <code>color</code>-variable for the border color for each highlighter section. </p>
<p>Now it all works as expected, as seen in this video:</p>
<video controls="" class="portrait-video">
<source src="https://videos.ctfassets.net/mpqufjsy02zr/276754Rs7AbUSxMkUCYoyS/cd83be97e1d48708bff21209c756cc59/keyboard-improved-en.mp4" type="video/mp4" />
</video>
<p>The video shows that focus and the label text change when a user navigates by using a keyboard.</p>
<p><a href="https://github.com/eevajonnapanula/graph-accessibility-example/commit/5e98304d1d82b53eea9241c3c8bd7a7b1943a1d4">You can find all the changes made in this commit.</a></p>
<h2 id="wrapping-up">Wrapping Up</h2>
<p>In this blog post, we've developed the <code>Highlighter</code>-component further to allow interaction with assistive devices like hardware keyboards. This way, a user won't be relying only on being able to use pointer input, such as touch. </p>
<p>The third blog post will be about differentiating data with other means than color. </p>
<h2 id="links-in-the-blog-post">Links in the Blog Post</h2>
<ul>
<li><a href="https://eevis.codes/blog/2023-07-24/more-accessible-graphs-with-jetpack-compose-part-1-adding-content/">More Accessible Graphs with Jetpack Compose Part 1: Adding Content Description</a></li>
<li><a href="https://github.com/eevajonnapanula/graph-accessibility-example/commit/5e98304d1d82b53eea9241c3c8bd7a7b1943a1d4">You can find all the changes made in this commit.</a></li>
</ul>
</article>
The Power of Default2023-08-07T00:00:00.000Zhttps://eevis.codes/blog/2023-08-07/the-power-of-default/
<nav id="breadcrumbs" aria-label="breadcrumbs">
<ol>
<li>
<a href="https://eevis.codes/blog/">Blog</a>
<span aria-hidden="true" class="breadcrumb-separator">/<span>
</span></span></li>
<li>The Power of Default</li><li>
</li></ol>
</nav>
<article class="blogPostLong">
<h1 id="/2023-08-07/the-power-of-default">The Power of Default</h1>
<div class="reading-time-and-date">
<p>
Reading time: about 6 minutes
</p>
<span class="separator"></span>
<p>Published <time datetime="7th Aug, 2023">7th Aug, 2023</time></p>
</div>
<section class="likes-and-boosts">
<div>
<svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="boosts-title">
<title id="boosts-title">Boosts</title>
<path d="M7.19 4.35047C5.15 6.64047 3.75 9.93047 3.62 10.2405L0 8.69047L4.05 4.64047C4.52 4.17047 5.2 3.96047 5.86 4.09047L7.19 4.35047ZM9.17 15.0005C9.17 15.0005 12.91 13.4505 15.06 11.3005C20.46 5.90047 19.56 1.68047 19.27 0.730473C18.32 0.430473 14.1 -0.459527 8.7 4.94047C6.55 7.09047 5 10.8305 5 10.8305L9.17 15.0005ZM15.65 12.8105C13.36 14.8505 10.07 16.2505 9.76 16.3805L11.31 20.0005L15.36 15.9505C15.83 15.4805 16.04 14.8005 15.91 14.1405L15.65 12.8105ZM7 16.0005C7 16.8305 6.66 17.5805 6.12 18.1205C4.94 19.3005 0 20.0005 0 20.0005C0 20.0005 0.7 15.0605 1.88 13.8805C2.2988 13.4595 2.83325 13.1724 3.4155 13.0558C3.99775 12.9392 4.60153 12.9982 5.15017 13.2253C5.69882 13.4525 6.16758 13.8376 6.49694 14.3317C6.8263 14.8258 7.0014 15.4067 7 16.0005ZM11 7.00047C11 5.90047 11.9 5.00047 13 5.00047C14.1 5.00047 15 5.90047 15 7.00047C15 8.10047 14.1 9.00047 13 9.00047C11.9 9.00047 11 8.10047 11 7.00047Z" fill="#00696D"></path>
</svg>
0
</div>
<div>
<svg role="img" width="20" height="19" viewBox="0 0 20 19" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="likes-title">
<title id="likes-title">Likes</title>
<path d="M10 18.35L8.55 17.03C3.4 12.36 0 9.27 0 5.5C0 2.41 2.42 0 5.5 0C7.24 0 8.91 0.81 10 2.08C11.09 0.81 12.76 0 14.5 0C17.58 0 20 2.41 20 5.5C20 9.27 16.6 12.36 11.45 17.03L10 18.35Z"></path>
</svg>
0
</div>
</section>
<img class="cover-photo" src="https://images.ctfassets.net/mpqufjsy02zr/4RsD2zmnyIm79kMP73KNIj/591c909cc8e6c2186d822013a80ceb17/the_power_of_default.png" alt="The Power of Default" />
<div class="blog-tags">
<a href="https://eevis.codes/tags/DEI"><span class="blog-tag blue">DEI</span></a>
</div>
<nav aria-label="The Power of Default Table of Contents" class="table-of-contents">
<h2>Table of Contents</h2>
<ol>
<li><a href="https://eevis.codes/blog/2023-08-07/the-power-of-default/#who-is-default-and-who-is-not"> Who is Default and Who is Not?</a></li><li><a href="https://eevis.codes/blog/2023-08-07/the-power-of-default/#the-default-in-tech"> The Default in Tech</a></li><li><a href="https://eevis.codes/blog/2023-08-07/the-power-of-default/#using-the-power-of-default-for-good"> Using the Power of Default for Good</a></li><li><a href="https://eevis.codes/blog/2023-08-07/the-power-of-default/#links-in-the-blog-post"> Links in the Blog Post</a></li>
</ol>
</nav>
<p><em>Oh, hey there! Before you read this blog post, I want to say that it contains a lot of talk about equality and how some groups of people are not seen as equal compared to others. I'm passionate about changing that. If you know that a feminist point of view makes you angry, I recommend reading but take some deep breaths before commenting. Try to be friendly, like there was actual human reading your comment. Thank you!</em></p>
<p>There's a lot of power with the default. And with default, I mean default selections in (digital) services, apps, and others - but also in the fact that some groups of people are seen as the default, "normal", compared to other groups.</p>
<p>Why I'm writing about this theme? Well, I believe that we, as developers (or, basically, any role in the tech industry), need to be really conscious of this topic so that we don't accidentally (or, in some cases, on purpose) exclude, or even discriminate, some of our users. We need to recognize what our decisions can do to actual people. </p>
<p>In this blog post, I'll first talk about who is default and who is not, then about the default in tech, and finally, I'll discuss using the default for good. </p>
<h2 id="who-is-default-and-who-is-not">Who is Default and Who is Not?</h2>
<h3 id="the-default-in-what-we-dont-say">The Default in What We Don't Say</h3>
<p>The default is visible in what we don't say and what needs to be explicitly said. Let's take an example from sports: When writing this post, the FIFA Women's World Cup is ongoing. Do you know what the men's tournament is called? FIFA World Cup. There is no mention of men; it's just an assumption that everyone knows it's about men; it's the default.</p>
<p>However, I want to mention that I love how Finnish YLE (the national broadcasting company) is talking just about "Jalkapallon MM 2023", meaning Football/Soccer World Championships 2023 — so they don't mention gender.</p>
<p>That is, of course, a problem for some; some Finnish people have complained online that it should be called <em>women's</em> football. Well, the same people have a history of saying that they're defending women's rights in sports when they're just being transphobic. Talk about caring about women's sports now.</p>
<p>The other example of the default is the color of skin. Have you ever noticed that when someone is describing a person, they often mention the color of the skin only if it's not white? It's as if white skin color is a default.</p>
<p>The third example I want to highlight is heteronormativity and its defaults. If someone says they're in love and has not explicitly told that they're not heterosexual, others often assume that if that person is a woman, the person she's in love with is a man, and vice versa. To be seen, they'll need to tell that they're, e.g., gay/bi/pansexual (or something else). The default assumption is that everyone is heterosexual.</p>
<p>And yes, heteronormative assumptions often forget that gender is not binary - which makes other genders than men and women invisible. </p>
<h3 id="the-default-with-professions">The Default with Professions</h3>
<p>As a woman in tech - and especially in a technical role, I constantly face the fact that the word "software developer" is a gendered word for many. There's this implicit assumption that developers are men. This belief is visible in how people speak - it's not once or twice when the hypothetical software developer is gendered as "he" (not, e.g., "they). It's also visible when I meet new people - there have been situations where I've assumed to be in a non-technical role, such as HR or marketing, or a designer, just because of my gender. </p>
<p>And this default with professions is detectable in other disciplines as well. It can be created through words, or it can be implicit. In Finland (and in many other countries), nurses are assumed to be women. Firefighters (no, I'm not going to enforce the stereotype by calling them "firemen") are often assumed to be men. </p>
<p>If we talk about high-paying roles, the assumption is that these people are men. There's this joke I've heard: "Women just don't want to be in the high-paying positions, they choose the lower paying ones, such as woman-CEO, woman-doctor, woman-lawyer, etc."</p>
<p>Defaults with professions are usually implied, something that's not said out loud. I gave examples from gender - but the other aspects have their own defaults. They all work the same way - often whiteness, cis-gender, heterosexuality, living without a disability, and other aspects are assumed.</p>
<h2 id="the-default-in-tech">The Default in Tech</h2>
<p>The other part of the power of the default I wanted to discuss is the default in and with tech. There are lots of occasions where the default selection affects how we operate - and that's often intentional. The creators of services know how the default selections affect us, so they utilize them, often for profit. </p>
<p>Sara Wachter-Boettcher discusses the default settings in her book "Technically Wrong: Sexist Apps, Biased Algorithms, and Other Threats of Toxic Tech." She writes:</p>
<blockquote>
<p>Defaults also affect how we perceive our choices, making us more likely to choose whatever is presented as default, and less likely to switch to something else. This is known as the default effect.</p>
</blockquote>
<p>And this is exploited often. It can be tip amounts defaulting to one of the higher options or preselecting accepting marketing emails. Or it can be the preselection of the pricing option in the middle when subscribing to a service. Of course, defaults can make our paths on these services faster - sometimes, it's just about that. But unfortunately, often, it's part of a design to profit more.</p>
<p>Sara Wachter-Boettcher also writes about the defaults in voice assistants like Apple's Siri, Google Now, and Amazon's Alexa. The default voice was a woman's for a long time for all of them. Apple switched to a non-woman default voice some major updates ago, but the other two have a woman's voice on by default. </p>
<p>As these voice assistants are, as the name states, assistants, it's as if the default gender for assistant, helper, should be a woman. And with this, I mean in the eyes of the creators. </p>
<p>In general, there's a lot to unpack about these voice assistants. If you're interested, there's a publication about the problematic design of these assistants: <a href="https://unesdoc.unesco.org/ark:/48223/pf0000367416.page=1">UNESCO and EQUAL Skills Coalition: I'd blush if I could: closing gender divides in digital skills through education</a>, specifically the Think Piece 2-chapter. </p>
<p>And as a final example for this section, have you ever paid attention to the default options of forms when, e.g., registering on a service? If they have default options and ask for gender, the preselection is usually "man." And if they ask for other things, it's often one of the abovementioned aspects, which I've mentioned as the default. </p>
<h2 id="using-the-power-of-default-for-good">Using the Power of Default for Good</h2>
<p>The nice thing about the power of default is that we can also utilize it for good. As developers, designers, and in other roles in the tech sector, we can change the defaults to more inclusive options. We can ensure these defaults are not used for discrimination and exclusion.</p>
<p>We can also affect how the technology itself behaves. As Caroline Criado Perez writes in her famous book "Invisible women", we have the data about inequality in our systems. "(but) whether or not coders will use it to fix their male-biased algorithms remains to be seen", she continues. In this case, she was writing about translations and how gender-neutral sentences were translated into English stereotypes - like Finnish "Hän on koodari," which is gender-neutral, would often be translated into "He is a coder." </p>
<p>We have the data about inequality; we have studies; we have what we need. Are we going to work on changing the default, which has for too long been a white, cis-gendered, heterosexual man without disabilities? We have the power to change that. All we need to do is pay attention, educate ourselves, and act.</p>
<p>Oh, and by the way, if you fall into the categories that I described above being the default, and especially if you fall into all of them: You probably can do the most, so I'm counting on you to work towards a more equal world and using your power to do so!</p>
<h2 id="links-in-the-blog-post">Links in the Blog Post</h2>
<ul>
<li><a href="https://unesdoc.unesco.org/ark:/48223/pf0000367416.page=1">UNESCO and EQUAL Skills Coalition: I'd blush if I could: closing gender divides in digital skills through education</a></li>
</ul>
</article>
More Accessible Graphs with Jetpack Compose Part 3: Differentiating without Color2023-08-16T00:00:00.000Zhttps://eevis.codes/blog/2023-08-16/more-accessible-graphs-with-jetpack-compose-part-3-differentiating-without/
<nav id="breadcrumbs" aria-label="breadcrumbs">
<ol>
<li>
<a href="https://eevis.codes/blog/">Blog</a>
<span aria-hidden="true" class="breadcrumb-separator">/<span>
</span></span></li>
<li>More Accessible Graphs with Jetpack Compose Part 3: Differentiating without Color</li><li>
</li></ol>
</nav>
<article class="blogPostLong">
<h1 id="/2023-08-16/more-accessible-graphs-with-jetpack-compose-part-3-differentiating-without">More Accessible Graphs with Jetpack Compose Part 3: Differentiating without Color</h1>
<div class="reading-time-and-date">
<p>
Reading time: about 5 minutes
</p>
<span class="separator"></span>
<p>Published <time datetime="16th Aug, 2023">16th Aug, 2023</time></p>
</div>
<section class="likes-and-boosts">
<div>
<svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="boosts-title">
<title id="boosts-title">Boosts</title>
<path d="M7.19 4.35047C5.15 6.64047 3.75 9.93047 3.62 10.2405L0 8.69047L4.05 4.64047C4.52 4.17047 5.2 3.96047 5.86 4.09047L7.19 4.35047ZM9.17 15.0005C9.17 15.0005 12.91 13.4505 15.06 11.3005C20.46 5.90047 19.56 1.68047 19.27 0.730473C18.32 0.430473 14.1 -0.459527 8.7 4.94047C6.55 7.09047 5 10.8305 5 10.8305L9.17 15.0005ZM15.65 12.8105C13.36 14.8505 10.07 16.2505 9.76 16.3805L11.31 20.0005L15.36 15.9505C15.83 15.4805 16.04 14.8005 15.91 14.1405L15.65 12.8105ZM7 16.0005C7 16.8305 6.66 17.5805 6.12 18.1205C4.94 19.3005 0 20.0005 0 20.0005C0 20.0005 0.7 15.0605 1.88 13.8805C2.2988 13.4595 2.83325 13.1724 3.4155 13.0558C3.99775 12.9392 4.60153 12.9982 5.15017 13.2253C5.69882 13.4525 6.16758 13.8376 6.49694 14.3317C6.8263 14.8258 7.0014 15.4067 7 16.0005ZM11 7.00047C11 5.90047 11.9 5.00047 13 5.00047C14.1 5.00047 15 5.90047 15 7.00047C15 8.10047 14.1 9.00047 13 9.00047C11.9 9.00047 11 8.10047 11 7.00047Z" fill="#00696D"></path>
</svg>
0
</div>
<div>
<svg role="img" width="20" height="19" viewBox="0 0 20 19" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="likes-title">
<title id="likes-title">Likes</title>
<path d="M10 18.35L8.55 17.03C3.4 12.36 0 9.27 0 5.5C0 2.41 2.42 0 5.5 0C7.24 0 8.91 0.81 10 2.08C11.09 0.81 12.76 0 14.5 0C17.58 0 20 2.41 20 5.5C20 9.27 16.6 12.36 11.45 17.03L10 18.35Z"></path>
</svg>
1
</div>
</section>
<img class="cover-photo" src="https://images.ctfassets.net/mpqufjsy02zr/7aIt8MbcdOKbiUouliYDTa/dd3f2e19dc508ab445943d70fd058370/more-accessible-graphs-3.png" alt="More Accessible Graphs with Jetpack Compose Part 3: Differentiating without Color." />
<div class="blog-tags">
<a href="https://eevis.codes/tags/Kotlin"><span class="blog-tag turquoise">Kotlin</span></a> <a href="https://eevis.codes/tags/a11y"><span class="blog-tag turquoise">a11y</span></a> <a href="https://eevis.codes/tags/Android"><span class="blog-tag blue">Android</span></a>
</div>
<nav aria-label="More Accessible Graphs with Jetpack Compose Part 3: Differentiating without Color Table of Contents" class="table-of-contents">
<h2>Table of Contents</h2>
<ol>
<li><a href="https://eevis.codes/blog/2023-08-16/more-accessible-graphs-with-jetpack-compose-part-3-differentiating-without/#options-for-color"> Options for Color</a></li><li><a href="https://eevis.codes/blog/2023-08-16/more-accessible-graphs-with-jetpack-compose-part-3-differentiating-without/#the-code"> The Code </a></li><li><a href="https://eevis.codes/blog/2023-08-16/more-accessible-graphs-with-jetpack-compose-part-3-differentiating-without/#wrapping-up"> Wrapping Up</a></li><li><a href="https://eevis.codes/blog/2023-08-16/more-accessible-graphs-with-jetpack-compose-part-3-differentiating-without/#links-in-the-blog-post"> Links in the Blog Post</a></li>
</ol>
</nav>
<p>This blog post is the third one in my series on more accessible graphs with Jetpack Compose. You can find the previous two from the following links:</p>
<ul>
<li><a href="https://eevis.codes/blog/2023-07-24/more-accessible-graphs-with-jetpack-compose-part-1-adding-content/">More Accessible Graphs with Jetpack Compose Part 1: Adding Content Description</a></li>
<li><a href="https://eevis.codes/blog/2023-07-31/more-accessible-graphs-with-jetpack-compose-part-2-adding-keyboard-interaction/">More Accessible Graphs with Jetpack Compose Part 2: Adding Keyboard Interaction</a></li>
</ul>
<p>The third topic we will cover is differentiating data by other means than color. Color is a convenient way to distinguish data, but what if you can't see color? Or what if you see colors differently? For example, the example project we've been developing during the blog post series would look like this for someone who can't see color at all:</p>
<img src="https://images.ctfassets.net/mpqufjsy02zr/MdDucPsdO0WO3ChjvUqIV/58c95e4f28d3ddb1059b6df2d4838ed5/Screenshot_2023-08-13_at_7.00.51.png" alt="An example how the app looks for someone who can't see color at all. Lines are not distinguishable from each other." class="portrait-img" />
<p>And someone with red-green color blindness:</p>
<img src="https://images.ctfassets.net/mpqufjsy02zr/3BiLAMvGDnBiBG6DLFPSmB/3c78c1052ea2c79eb61593802adb5220/Screenshot_2023-08-13_at_7.00.32.png" alt="An example how the app looks for someone with red-green color blindness. Lines are not distinguishable from each other." class="portrait-img" />
<p>Another aspect of why not use color alone is that phone screens display color differently. A noticeable difference on a high-end external screen for a designer might be almost the same shades for a lower-end phone, making usage impossible. </p>
<p>There are better ideas than just using color to differentiate data. In the next section, let's explore some of these other ways.</p>
<h2 id="options-for-color">Options for Color</h2>
<p>When talking about graphs, there are generally two ways to improve the distinguishability of data: Shapes and patterns. For example, if a bar chart is initially differentiated by color, adding patterns for different colors could differentiate those bars. </p>
<p>For a line chart, both these options are valid. Let's explore both and differentiate the data by adding different shapes for points and some patterns for the line. This solution is not the most beautiful because I'm mixing these two options, but it's there to demonstrate, not to be a final design.</p>
<h2 id="the-code">The Code</h2>
<p>In the situation we're starting with, the function we're using to draw the data points looks like this:</p>
<pre><code class="language-kotlin"><span class="token function">drawPoints</span><span class="token punctuation">(</span>
points <span class="token operator">=</span> pixelPoints<span class="token punctuation">.</span><span class="token function">map</span> <span class="token punctuation">{</span> <span class="token function">Offset</span><span class="token punctuation">(</span>it<span class="token punctuation">.</span>x <span class="token operator">+</span> <span class="token number">20f</span><span class="token punctuation">,</span> it<span class="token punctuation">.</span>y<span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
pointMode <span class="token operator">=</span> PointMode<span class="token punctuation">.</span>Points<span class="token punctuation">,</span>
color <span class="token operator">=</span> color<span class="token punctuation">,</span>
strokeWidth <span class="token operator">=</span> <span class="token number">8</span><span class="token punctuation">.</span>dp<span class="token punctuation">.</span><span class="token function">toPx</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
cap <span class="token operator">=</span> StrokeCap<span class="token punctuation">.</span>Round<span class="token punctuation">,</span>
<span class="token punctuation">)</span>
</code></pre><p>It takes the points and point mode from the parameters, and other than that, everything is defined inside the helper function. In this example, we're interested in the <code>strokeWidth</code> and <code>cap</code>-properties because we're using them to change how the data points are drawn. In addition, we will modify the code for drawing the path between the data points by adding an additional style. </p>
<p>Let's get started with changing the shape of the data points. First, let's add new parameters for the <code>drawData</code>-extension, which encapsulates the data points and line's drawing methods:</p>
<pre><code class="language-kotlin"><span class="token keyword">fun</span> DrawScope<span class="token punctuation">.</span><span class="token function">drawData</span><span class="token punctuation">(</span>
<span class="token operator">..</span><span class="token punctuation">.</span>
strokeWidth<span class="token operator">:</span> Float <span class="token operator">=</span> <span class="token number">8</span><span class="token punctuation">.</span>dp<span class="token punctuation">.</span><span class="token function">toPx</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
strokeCap<span class="token operator">:</span> StrokeCap <span class="token operator">=</span> StrokeCap<span class="token punctuation">.</span>Round
<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token operator">..</span><span class="token punctuation">.</span> <span class="token punctuation">}</span>
</code></pre><p>We add two new parameters: <code>strokeWidth</code> and <code>strokeCap,</code> and then set the current values as defaults. Then we use these new properties in the code:</p>
<pre><code class="language-kotlin"><span class="token function">drawPoints</span><span class="token punctuation">(</span>
<span class="token operator">..</span><span class="token punctuation">.</span>
strokeWidth <span class="token operator">=</span> strokeWidth<span class="token punctuation">,</span>
cap <span class="token operator">=</span> strokeCap
<span class="token punctuation">)</span>
</code></pre><p>Note: We add these for both of the <code>drawPoints</code>-methods, as there is one for highlighted points and one for all points. </p>
<p>Then, in the <code>Graph</code>-composable, we add the actual differentiation for each data set:</p>
<pre><code class="language-kotlin"><span class="token function">drawData</span><span class="token punctuation">(</span>
<span class="token operator">..</span><span class="token punctuation">.</span> <span class="token comment">// Engineering data</span>
strokeCap <span class="token operator">=</span> StrokeCap<span class="token punctuation">.</span>Round<span class="token punctuation">,</span>
<span class="token punctuation">)</span>
<span class="token function">drawData</span><span class="token punctuation">(</span>
<span class="token operator">..</span><span class="token punctuation">.</span> <span class="token comment">// ICT-data</span>
strokeCap <span class="token operator">=</span> StrokeCap<span class="token punctuation">.</span>Square<span class="token punctuation">,</span>
<span class="token punctuation">)</span>
<span class="token function">drawData</span><span class="token punctuation">(</span>
<span class="token operator">..</span><span class="token punctuation">.</span> <span class="token comment">// Total data</span>
strokeCap <span class="token operator">=</span> StrokeCap<span class="token punctuation">.</span>Butt<span class="token punctuation">,</span>
strokeWidth <span class="token operator">=</span> <span class="token number">12</span><span class="token punctuation">.</span>dp<span class="token punctuation">.</span><span class="token function">toPx</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
<span class="token punctuation">)</span>
</code></pre><p>As we set the defaults, we don't need to pass a value for both parameters if the value is the default value. That's the case for the ICT and Engineering data, as we want to keep the point size the same. Actually, we wouldn't need to pass the <code>strokeCap</code>-value for the Engineering data either for the same reason - but I chose to add it for clarity and to display the different values. </p>
<p>Okay, after these changes, the points look different. For Engineering data, nothing has changed, but for ICT data and total data, the points are rectangular, and the total data has more significant points than the others. </p>
<p>Let's add another differentiator before we look at the results. We want also to be able to distinguish the lines from each other, and one way to do that is by changing the pattern of the line. In this example, we will add a dashed line for one of the data sets. </p>
<p>Let's start with adding another new parameter for <code>drawData</code>:</p>
<pre><code class="language-kotlin"><span class="token keyword">fun</span> DrawScope<span class="token punctuation">.</span><span class="token function">drawData</span><span class="token punctuation">(</span>
dashed<span class="token operator">:</span> Boolean <span class="token operator">=</span> <span class="token boolean">false</span><span class="token punctuation">,</span>
<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token operator">..</span><span class="token punctuation">.</span> <span class="token punctuation">}</span>
</code></pre><p>The new parameter is <code>dashed,</code> and it's set to false by default. When it's true, we want to change the style of the path that we're drawing:</p>
<pre><code class="language-kotlin"><span class="token function">drawPath</span><span class="token punctuation">(</span>
path <span class="token operator">=</span> path<span class="token punctuation">,</span>
color <span class="token operator">=</span> color<span class="token punctuation">,</span>
style <span class="token operator">=</span> <span class="token function">Stroke</span><span class="token punctuation">(</span>
width <span class="token operator">=</span> <span class="token number">3f</span><span class="token punctuation">,</span>
pathEffect <span class="token operator">=</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>dashed<span class="token punctuation">)</span>
PathEffect<span class="token punctuation">.</span><span class="token function">dashPathEffect</span><span class="token punctuation">(</span><span class="token function">floatArrayOf</span><span class="token punctuation">(</span><span class="token number">10f</span><span class="token punctuation">,</span> <span class="token number">10f</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token number">0f</span><span class="token punctuation">)</span>
<span class="token keyword">else</span> <span class="token keyword">null</span>
<span class="token punctuation">)</span><span class="token punctuation">,</span>
<span class="token punctuation">)</span>
</code></pre><p>We add the<code>pathEffect</code> for the stroke and set it to a dashed path effect with intervals of <code>10f</code>. If <code>dashed</code> is false, then we don't set the path effect - or we set it to null, which is its default value.</p>
<p>The last thing to do is to pass the value to one of the <code>drawData</code>-functions, and in this case, we pass it to the ICT data:</p>
<pre><code class="language-kotlin"><span class="token function">drawData</span><span class="token punctuation">(</span>
<span class="token operator">..</span><span class="token punctuation">.</span> <span class="token comment">// ICT-data</span>
strokeCap <span class="token operator">=</span> StrokeCap<span class="token punctuation">.</span>Square<span class="token punctuation">,</span>
dashed <span class="token operator">=</span> <span class="token boolean">true</span>
<span class="token punctuation">)</span>
</code></pre><p>And with that, we have added both shapes and a pattern. The final implementation looks like this:</p>
<img src="https://images.ctfassets.net/mpqufjsy02zr/6SeTD2Z7UWsgaVy2ChQBEz/0d11d79ee14d6ee6be7fe7cf5cb394ea/Screenshot_20230813_070914.png" alt="The lines have now different point-styles and one of the datasets has a dashed line." class="portrait-img" />
<p><a href="https://github.com/eevajonnapanula/graph-accessibility-example/commit/66f3744cc12c84480e0ba4ab6e85130d41d379aa">You can find the final code from this commit.</a></p>
<h2 id="wrapping-up">Wrapping Up</h2>
<p>In this blog post, we have looked at improving differentiating data by other means than color. We've covered adding shapes and patterns by changing the data points in the line chart to have different shapes and adding a dashed pattern to one of the lines. </p>
<p>The fourth post in this series will cover switch devices and how navigation should work with them. Publishing it will take some time - I'll need to find out why it wasn't working with the solution it should have. </p>
<h2 id="links-in-the-blog-post">Links in the Blog Post</h2>
<ul>
<li><a href="https://eevis.codes/blog/2023-07-24/more-accessible-graphs-with-jetpack-compose-part-1-adding-content/">More Accessible Graphs with Jetpack Compose Part 1: Adding Content Description</a></li>
<li><a href="https://eevis.codes/blog/2023-07-31/more-accessible-graphs-with-jetpack-compose-part-2-adding-keyboard-interaction/">More Accessible Graphs with Jetpack Compose Part 2: Adding Keyboard Interaction</a></li>
<li><a href="https://github.com/eevajonnapanula/graph-accessibility-example/commit/66f3744cc12c84480e0ba4ab6e85130d41d379aa">You can find the final code from this commit.</a></li>
</ul>
</article>
Understanding Density-Independent Pixels2023-09-04T00:00:00.000Zhttps://eevis.codes/blog/2023-09-04/understanding-density-independent-pixels/
<nav id="breadcrumbs" aria-label="breadcrumbs">
<ol>
<li>
<a href="https://eevis.codes/blog/">Blog</a>
<span aria-hidden="true" class="breadcrumb-separator">/<span>
</span></span></li>
<li>Understanding Density-Independent Pixels</li><li>
</li></ol>
</nav>
<article class="blogPostLong">
<h1 id="/2023-09-04/understanding-density-independent-pixels">Understanding Density-Independent Pixels</h1>
<div class="reading-time-and-date">
<p>
Reading time: about 3 minutes
</p>
<span class="separator"></span>
<p>Published <time datetime="4th Sep, 2023">4th Sep, 2023</time></p>
</div>
<section class="likes-and-boosts">
<div>
<svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="boosts-title">
<title id="boosts-title">Boosts</title>
<path d="M7.19 4.35047C5.15 6.64047 3.75 9.93047 3.62 10.2405L0 8.69047L4.05 4.64047C4.52 4.17047 5.2 3.96047 5.86 4.09047L7.19 4.35047ZM9.17 15.0005C9.17 15.0005 12.91 13.4505 15.06 11.3005C20.46 5.90047 19.56 1.68047 19.27 0.730473C18.32 0.430473 14.1 -0.459527 8.7 4.94047C6.55 7.09047 5 10.8305 5 10.8305L9.17 15.0005ZM15.65 12.8105C13.36 14.8505 10.07 16.2505 9.76 16.3805L11.31 20.0005L15.36 15.9505C15.83 15.4805 16.04 14.8005 15.91 14.1405L15.65 12.8105ZM7 16.0005C7 16.8305 6.66 17.5805 6.12 18.1205C4.94 19.3005 0 20.0005 0 20.0005C0 20.0005 0.7 15.0605 1.88 13.8805C2.2988 13.4595 2.83325 13.1724 3.4155 13.0558C3.99775 12.9392 4.60153 12.9982 5.15017 13.2253C5.69882 13.4525 6.16758 13.8376 6.49694 14.3317C6.8263 14.8258 7.0014 15.4067 7 16.0005ZM11 7.00047C11 5.90047 11.9 5.00047 13 5.00047C14.1 5.00047 15 5.90047 15 7.00047C15 8.10047 14.1 9.00047 13 9.00047C11.9 9.00047 11 8.10047 11 7.00047Z" fill="#00696D"></path>
</svg>
0
</div>
<div>
<svg role="img" width="20" height="19" viewBox="0 0 20 19" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="likes-title">
<title id="likes-title">Likes</title>
<path d="M10 18.35L8.55 17.03C3.4 12.36 0 9.27 0 5.5C0 2.41 2.42 0 5.5 0C7.24 0 8.91 0.81 10 2.08C11.09 0.81 12.76 0 14.5 0C17.58 0 20 2.41 20 5.5C20 9.27 16.6 12.36 11.45 17.03L10 18.35Z"></path>
</svg>
0
</div>
</section>
<img class="cover-photo" src="https://images.ctfassets.net/mpqufjsy02zr/1u83mziTsTr84FjOzEJY4m/08a2c2a2835573c224baa4791d1f2576/Understanding_Density-Independent_Pixels.png" alt="Understanding Density-Independent Pixels" />
<div class="blog-tags">
<a href="https://eevis.codes/tags/Android"><span class="blog-tag blue">Android</span></a> <a href="https://eevis.codes/tags/Kotlin"><span class="blog-tag turquoise">Kotlin</span></a>
</div>
<nav aria-label="Understanding Density-Independent Pixels Table of Contents" class="table-of-contents">
<h2>Table of Contents</h2>
<ol>
<li><a href="https://eevis.codes/blog/2023-09-04/understanding-density-independent-pixels/#what-are-density-independent-pixels"> What Are Density-Independent Pixels?</a></li><li><a href="https://eevis.codes/blog/2023-09-04/understanding-density-independent-pixels/#conversions-between-pixels-and-density-independent-pixels"> Conversions Between Pixels and Density-Independent Pixels</a></li><li><a href="https://eevis.codes/blog/2023-09-04/understanding-density-independent-pixels/#wrapping-up"> Wrapping Up</a></li><li><a href="https://eevis.codes/blog/2023-09-04/understanding-density-independent-pixels/#read-more"> Read More</a></li>
</ol>
</nav>
<p>I switched to Android development from web front end, and one thing that was a bit hard to understand was how the density of Android screens affects the use of units. For web, I mainly worked with pixels, <code>em</code>s (the computed value of the element's font size), and <code>rem</code>s (the computed value of the root element's font size). </p>
<p>So, I wanted to write a blog post about this theme because I bet I'm not the only one. Let's first look at the density-independent pixels, what they are, and why they're used. After that, let's discuss the conversion between them and pixels. I've purposely omitted scale-independent pixels from this blog post to concentrate on density-independent pixels.</p>
<h2 id="what-are-density-independent-pixels">What Are Density-Independent Pixels?</h2>
<p>Android devices have different sizes of screens. They also have different pixel sizes for a screen - so a certain amount of pixels in, e.g., width, looks different on a screen with fewer pixels compared to one with a lot more pixels. This is because one screen might fit only 320 pixels in the same physical space while the other fits 640 pixels. </p>
<p>Density-independent pixels are here to fix the issues these differences cause. Android documentation defines density-independent pixels in the following way:</p>
<blockquote>
<p>To preserve the visible size of your UI on screens with different densities, design your UI using density-independent pixels (dp) as your unit of measurement. One dp is a virtual pixel unit that's roughly equal to one pixel on a medium-density screen (160 dpi, or the "baseline" density). Android translates this value to the appropriate number of real pixels for each other density.</p>
</blockquote>
<p>Source: <a href="https://developer.android.com/training/multiscreen/screendensities">Support different pixel densities - Android Developers</a></p>
<p>Let's next look at the conversions between real pixels and density-independent pixels.</p>
<h2 id="conversions-between-pixels-and-density-independent-pixels">Conversions Between Pixels and Density-Independent Pixels</h2>
<p>There are multiple cases when we need the conversion from device-independent pixels to pixels - and vice versa. Let's look at how we can convert them - first in the scope of Composable components and then in other cases.</p>
<h3 id="composable-components">Composable Components</h3>
<p>With Jetpack Compose, <code>LocalDensity</code> helps with conversions between pixels and density-independent pixels. <code>LocalDensity</code> is one of the built-in composition locals for Compose, and it provides a <code>Density</code> that can be used for conversions between pixels and density-independent pixels, as well as scale-independent pixels.</p>
<p>Let's look at how we can use it in a Composable component. First, conversion from pixels to device-independent pixels:</p>
<pre><code class="language-kotlin"><span class="token annotation builtin">@Composable</span>
<span class="token keyword">fun</span> <span class="token function">PxToDp</span><span class="token punctuation">(</span>widthInPx<span class="token operator">:</span> Int<span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">val</span> density <span class="token operator">=</span> LocalDensity<span class="token punctuation">.</span>current
<span class="token keyword">val</span> widthInDp <span class="token operator">=</span> <span class="token function">with</span> <span class="token punctuation">(</span>density<span class="token punctuation">)</span> <span class="token punctuation">{</span> widthInPx<span class="token punctuation">.</span><span class="token function">toDp</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">}</span>
<span class="token operator">..</span><span class="token punctuation">.</span>
<span class="token punctuation">}</span>
</code></pre><p>And then vice versa:</p>
<pre><code class="language-kotlin"><span class="token annotation builtin">@Composable</span>
<span class="token keyword">fun</span> <span class="token function">DpToPx</span><span class="token punctuation">(</span>widthInDp<span class="token operator">:</span> Int<span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">val</span> density <span class="token operator">=</span> LocalDensity<span class="token punctuation">.</span>current
<span class="token keyword">val</span> widthInPx <span class="token operator">=</span> <span class="token function">with</span> <span class="token punctuation">(</span>density<span class="token punctuation">)</span> <span class="token punctuation">{</span> widthInDp<span class="token punctuation">.</span><span class="token function">toPx</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">}</span>
<span class="token operator">..</span><span class="token punctuation">.</span>
<span class="token punctuation">}</span>
</code></pre><p>Both of the examples utilize Kotlin's <code>with</code>-scope function. In the scope of <code>Density</code> provided by <code>LocalDensity,</code> the extension functions for conversion are available, and we can use them. </p>
<h3 id="other-cases">Other Cases</h3>
<p>When not using Jetpack Compose and/or <code>LocalDensity</code> is unavailable, you can, for example, write your own extension functions for conversion. Here's an example with extension functions for <code>Float</code>-datatype:</p>
<pre><code class="language-kotlin"><span class="token keyword">fun</span> Float<span class="token punctuation">.</span><span class="token function">toDp</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=</span>
<span class="token punctuation">(</span><span class="token keyword">this</span> <span class="token operator">/</span> Resources<span class="token punctuation">.</span><span class="token function">getSystem</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span>displayMetrics<span class="token punctuation">.</span>density<span class="token punctuation">)</span>
<span class="token keyword">fun</span> Float<span class="token punctuation">.</span><span class="token function">toPx</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=</span>
<span class="token punctuation">(</span><span class="token keyword">this</span> <span class="token operator">*</span> Resources<span class="token punctuation">.</span><span class="token function">getSystem</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span>displayMetrics<span class="token punctuation">.</span>density<span class="token punctuation">)</span>
</code></pre><p>In both functions, the device's screen density is fetched from <code>Resources.getSystem().displayMetrics.density</code>. For the pixel-to-dp-conversion, a pixel value is divided by it, and for the dp-to-pixel-conversion, the dp value is multiplied by it. </p>
<h2 id="wrapping-up">Wrapping Up</h2>
<p>In this blog post, we've discussed device-independent pixels. We also looked at how to convert them to pixels and back. These conversions can be useful in many cases, such as when positioning elements on layouts. </p>
<p>Do you have any examples of when converting between them was needed? Or when it was more complex than you would have wished? Share your thoughts!</p>
<h2 id="read-more">Read More</h2>
<ul>
<li><a href="https://developer.android.com/training/multiscreen/screendensities">Support different pixel densities - Android Developers</a></li>
<li><a href="https://developer.android.com/reference/kotlin/androidx/compose/ui/unit/Density">Density - Android Developers</a></li>
</ul>
</article>
CatGPT - or How to Position Elements on Overlays2023-10-22T00:00:00.000Zhttps://eevis.codes/blog/2023-10-22/catgpt-or-how-to-position-elements-on-overlays/
<nav id="breadcrumbs" aria-label="breadcrumbs">
<ol>
<li>
<a href="https://eevis.codes/blog/">Blog</a>
<span aria-hidden="true" class="breadcrumb-separator">/<span>
</span></span></li>
<li>CatGPT - or How to Position Elements on Overlays</li><li>
</li></ol>
</nav>
<article class="blogPostLong">
<h1 id="/2023-10-22/catgpt-or-how-to-position-elements-on-overlays">CatGPT - or How to Position Elements on Overlays</h1>
<div class="reading-time-and-date">
<p>
Reading time: about 6 minutes
</p>
<span class="separator"></span>
<p>Published <time datetime="22nd Oct, 2023">22nd Oct, 2023</time></p>
</div>
<section class="likes-and-boosts">
<div>
<svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="boosts-title">
<title id="boosts-title">Boosts</title>
<path d="M7.19 4.35047C5.15 6.64047 3.75 9.93047 3.62 10.2405L0 8.69047L4.05 4.64047C4.52 4.17047 5.2 3.96047 5.86 4.09047L7.19 4.35047ZM9.17 15.0005C9.17 15.0005 12.91 13.4505 15.06 11.3005C20.46 5.90047 19.56 1.68047 19.27 0.730473C18.32 0.430473 14.1 -0.459527 8.7 4.94047C6.55 7.09047 5 10.8305 5 10.8305L9.17 15.0005ZM15.65 12.8105C13.36 14.8505 10.07 16.2505 9.76 16.3805L11.31 20.0005L15.36 15.9505C15.83 15.4805 16.04 14.8005 15.91 14.1405L15.65 12.8105ZM7 16.0005C7 16.8305 6.66 17.5805 6.12 18.1205C4.94 19.3005 0 20.0005 0 20.0005C0 20.0005 0.7 15.0605 1.88 13.8805C2.2988 13.4595 2.83325 13.1724 3.4155 13.0558C3.99775 12.9392 4.60153 12.9982 5.15017 13.2253C5.69882 13.4525 6.16758 13.8376 6.49694 14.3317C6.8263 14.8258 7.0014 15.4067 7 16.0005ZM11 7.00047C11 5.90047 11.9 5.00047 13 5.00047C14.1 5.00047 15 5.90047 15 7.00047C15 8.10047 14.1 9.00047 13 9.00047C11.9 9.00047 11 8.10047 11 7.00047Z" fill="#00696D"></path>
</svg>
0
</div>
<div>
<svg role="img" width="20" height="19" viewBox="0 0 20 19" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="likes-title">
<title id="likes-title">Likes</title>
<path d="M10 18.35L8.55 17.03C3.4 12.36 0 9.27 0 5.5C0 2.41 2.42 0 5.5 0C7.24 0 8.91 0.81 10 2.08C11.09 0.81 12.76 0 14.5 0C17.58 0 20 2.41 20 5.5C20 9.27 16.6 12.36 11.45 17.03L10 18.35Z"></path>
</svg>
0
</div>
</section>
<img class="cover-photo" src="https://images.ctfassets.net/mpqufjsy02zr/6GLnUvTcOMm9GxN46GBFEF/d0a335e59695bb51e74e1186a2d1643b/catgpt__1_.png" alt="" />
<div class="blog-tags">
<a href="https://eevis.codes/tags/Android"><span class="blog-tag blue">Android</span></a> <a href="https://eevis.codes/tags/Kotlin"><span class="blog-tag turquoise">Kotlin</span></a>
</div>
<nav aria-label="CatGPT - or How to Position Elements on Overlays Table of Contents" class="table-of-contents">
<h2>Table of Contents</h2>
<ol>
<li><a href="https://eevis.codes/blog/2023-10-22/catgpt-or-how-to-position-elements-on-overlays/#the-example-app"> The Example App</a></li><li><a href="https://eevis.codes/blog/2023-10-22/catgpt-or-how-to-position-elements-on-overlays/#showing-reaction-with-message"> Showing Reaction with Message</a></li><li><a href="https://eevis.codes/blog/2023-10-22/catgpt-or-how-to-position-elements-on-overlays/#the-reaction-picker"> The Reaction Picker</a></li><li><a href="https://eevis.codes/blog/2023-10-22/catgpt-or-how-to-position-elements-on-overlays/#wrapping-up"> Wrapping Up</a></li>
</ol>
</nav>
<p>Have you ever wondered how to display an element with an overlay while keeping it in the same position? The same way that, for example, many messaging apps highlight the message you want to react to. </p>
<p>I had this type of challenge in one of my work projects. It took some trial and error to get it working, but I was so proud of myself when I finally found the correct modifiers (and numbers). I want to share one way to do it with you in this blog post.</p>
<h2 id="the-example-app">The Example App</h2>
<p>Okay, I must say that I maybe, just maybe, got a little bit carried away with this example app. But it's a CatGPT - a chat client where you can ask anything from a cat. Here's a short video:</p>
<video controls="" class="portrait-video">
<source src="https://videos.ctfassets.net/mpqufjsy02zr/FQOnmA67pZheN5VmCIBip/dd42501e072af4811367885f3bf95465/catnip-video.mp4" type="video/mp4" />
</video>
<p>This app is simple; It doesn't use any third-party APIs, so the cat in question lives in the code. The app has one Room database, where it stores messages, and it has one screen called <code>ChatScreen</code> which displays messages.</p>
<p>If you want to see the code, the starting point for the app is in the <a href="https://github.com/eevajonnapanula/CatGPT/tree/starting-point"><code>starting-point</code>-branch</a>, and the final code is in <a href="https://github.com/eevajonnapanula/CatGPT">CatGPT-repository's main-branch</a>.</p>
<h2 id="showing-reaction-with-message">Showing Reaction with Message</h2>
<p>To simplify this app, each message can have only one reaction. In the <code>Message</code> data class, the reaction is already present: </p>
<pre><code class="language-kotlin"><span class="token comment">// data/Message.kt</span>
<span class="token annotation builtin">@Entity</span>
<span class="token keyword">data</span> <span class="token keyword">class</span> <span class="token function">Message</span><span class="token punctuation">(</span>
<span class="token annotation builtin">@PrimaryKey</span><span class="token punctuation">(</span>autoGenerate <span class="token operator">=</span> <span class="token boolean">true</span><span class="token punctuation">)</span>
<span class="token keyword">val</span> id<span class="token operator">:</span> Long <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">,</span>
<span class="token keyword">val</span> text<span class="token operator">:</span> String <span class="token operator">=</span> <span class="token string-literal singleline"><span class="token string">""</span></span><span class="token punctuation">,</span>
<span class="token keyword">val</span> sender<span class="token operator">:</span> Sender <span class="token operator">=</span> Sender<span class="token punctuation">.</span>ME<span class="token punctuation">,</span>
<span class="token annotation builtin">@TypeConverters</span><span class="token punctuation">(</span>ReactionConverter<span class="token operator">::</span><span class="token keyword">class</span><span class="token punctuation">)</span>
<span class="token keyword">val</span> reaction<span class="token operator">:</span> Reaction<span class="token operator">?</span> <span class="token operator">=</span> <span class="token keyword">null</span><span class="token punctuation">,</span>
<span class="token punctuation">)</span>
</code></pre><p>And it's a nullable Reaction-enum. The complete code and converters for Reaction are in <a href="https://github.com/eevajonnapanula/CatGPT/blob/main/app/src/main/java/com/eevajonna/catgpt/data/Message.kt"><code>Message.kt</code></a>. </p>
<p>Let's first add code for showing that reaction before building the actual reaction picker. In the <code>MessageRow.kt</code>-file, there is already a <code>ConstraintLayout</code>, which contains the component for the message. We want to show the reaction in the bottom right corner of that component, so we'll add the reaction inside the constraint layout. </p>
<p>The component for reaction looks like this: </p>
<pre><code class="language-kotlin"><span class="token comment">// ui/components/MessageRow.kt</span>
message<span class="token punctuation">.</span>reaction<span class="token operator">?</span><span class="token punctuation">.</span><span class="token function">let</span> <span class="token punctuation">{</span>
<span class="token function">Text</span><span class="token punctuation">(</span>
modifier <span class="token operator">=</span> Modifier
<span class="token punctuation">.</span><span class="token function">clip</span><span class="token punctuation">(</span>ChatScreen<span class="token punctuation">.</span>shape<span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">background</span><span class="token punctuation">(</span>MaterialTheme<span class="token punctuation">.</span>colorScheme<span class="token punctuation">.</span>surface<span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">padding</span><span class="token punctuation">(</span>MessageRow<span class="token punctuation">.</span>reactionPadding<span class="token punctuation">)</span>
text <span class="token operator">=</span> it<span class="token punctuation">.</span>emoji<span class="token punctuation">,</span>
<span class="token punctuation">)</span>
<span class="token punctuation">}</span>
</code></pre><p>As the reaction is a nullable string, we first check if it's available with Kotlin's <code>let</code>, and inside its block, we add the reaction as <code>Text</code>-element. </p>
<p>Now, the reaction is visible. We still want to position it correctly, so we'll first add the reaction as a reference for constraint:</p>
<pre><code class="language-kotlin"><span class="token comment">// ui/components/MessageRow.kt</span>
<span class="token comment">// From</span>
<span class="token keyword">val</span> <span class="token punctuation">(</span>text<span class="token punctuation">)</span> <span class="token operator">=</span> <span class="token function">createRefs</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token comment">// To</span>
<span class="token keyword">val</span> <span class="token punctuation">(</span>text<span class="token punctuation">,</span> reaction<span class="token punctuation">)</span> <span class="token operator">=</span> <span class="token function">createRefs</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
</code></pre><p>And then for the <code>Text</code>-component we just added, we'll add the <code>constrainAs</code>-modifier:</p>
<pre><code class="language-kotlin"><span class="token comment">// ui/components/MessageRow.kt</span>
message<span class="token punctuation">.</span>reaction<span class="token operator">?</span><span class="token punctuation">.</span><span class="token function">let</span> <span class="token punctuation">{</span>
<span class="token function">Text</span><span class="token punctuation">(</span>
modifier <span class="token operator">=</span> Modifier
<span class="token operator">..</span><span class="token punctuation">.</span>
<span class="token punctuation">.</span><span class="token function">constrainAs</span><span class="token punctuation">(</span>reaction<span class="token punctuation">)</span> <span class="token punctuation">{</span>
end<span class="token punctuation">.</span><span class="token function">linkTo</span><span class="token punctuation">(</span>text<span class="token punctuation">.</span>end<span class="token punctuation">,</span> MessageRow<span class="token punctuation">.</span>reactionPadding<span class="token punctuation">)</span>
bottom<span class="token punctuation">.</span><span class="token function">linkTo</span><span class="token punctuation">(</span>text<span class="token punctuation">.</span>bottom<span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
text <span class="token operator">=</span> it<span class="token punctuation">,</span>
<span class="token punctuation">)</span>
<span class="token punctuation">}</span>
</code></pre><p>Inside the <code>constrainAs</code>-modifier, we constrain the end of the component to the end of the message text with a bit of padding and the bottom of the reaction to the bottom of the message text. After these changes, the message component with a reaction would look like this:</p>
<p><img src="https://images.ctfassets.net/mpqufjsy02zr/2Hf2V8YxpCO4PKzS2C24tb/1782e841b70f1a15801a8a006c229aaf/Screenshot_2023-10-20_at_7.08.09.png" alt="Text "Meow meow meow meow meow meow" on dark pink background. On the bottom right corner, there is a grinning cat with smiley eyes." /></p>
<p>All the <a href="https://github.com/eevajonnapanula/CatGPT/commit/3213f53c2dec17250e65ee6db7938feee7dcd847">changes for showing the reaction with the message are in this commit.</a></p>
<h2 id="the-reaction-picker">The Reaction Picker</h2>
<p>Alright. Now we're showing the reactions - but to have something to display, there should be a way to add a reaction. We will do it with an overlay with a blurred background and a component that shows the selected message and available reactions.</p>
<h3 id="getting-the-messages-y-position">Getting the Message's Y-Position</h3>
<p>We first want to get the y-position of the message component in the conversation to position the element correctly in the overlay we're adding. We can do it by using the <code>onGloballyPositioned</code>-modifier and storing the y-value to a state variable. For that, we also need to know the density of the user's phone so that we can convert between the pixels and density-independent pixels:</p>
<pre><code class="language-kotlin"><span class="token comment">// MessageRow.kt</span>
<span class="token keyword">val</span> density <span class="token operator">=</span> LocalDensity<span class="token punctuation">.</span>current
<span class="token keyword">var</span> yPosition <span class="token keyword">by</span> remember <span class="token punctuation">{</span> <span class="token function">mutableStateOf</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">.</span>dp<span class="token punctuation">)</span> <span class="token punctuation">}</span>
</code></pre><p>If you're wondering about the need for density or need a refresher on density-independent pixels, I've written a blog post about them: <a href="https://eevis.codes/blog/2023-09-04/understanding-density-independent-pixels/">Understanding Density-Independent Pixels</a>.</p>
<p>Okay, now we have what we need to get the actual position:</p>
<pre><code class="language-kotlin"><span class="token comment">// MessageRow.kt</span>
<span class="token function">ConstraintLayout</span><span class="token punctuation">(</span>
modifier <span class="token operator">=</span> Modifier<span class="token punctuation">.</span>
<span class="token punctuation">.</span><span class="token function">fillMaxWidth</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">onGloballyPositioned</span> <span class="token punctuation">{</span>
yPosition <span class="token operator">=</span> <span class="token function">with</span> <span class="token punctuation">(</span>density<span class="token punctuation">)</span> <span class="token punctuation">{</span>
it<span class="token punctuation">.</span><span class="token function">positionInParent</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span>y<span class="token punctuation">.</span><span class="token function">toDp</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">+</span>
MessageRow<span class="token punctuation">.</span>reactionDialogOffset <span class="token operator">+</span>
MessageRow<span class="token punctuation">.</span>reactionPadding
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token operator">..</span><span class="token punctuation">.</span> <span class="token punctuation">}</span>
</code></pre><p>We use the <code>.onGloballyPositioned</code>-modifier to get the y-position. <code>positionInParent()</code> returns pixels, and to get the density-independent pixels for position, we use the <code>with</code>-scope function to convert them. We also add a bit of offset and padding to match the position because the original message component has those offset and padding. </p>
<h3 id="reaction-picker-component">Reaction Picker Component</h3>
<p>After the changes in the previous section, we have the y-position of the message. The next thing we need to do is to create the reaction picker component and pass the y-position to that component. After that, we can actually place the element in the overlay. </p>
<p>The component inside the overlay is a <code>ConstraintLayout</code>, which wraps a <code>MessageBlock</code> that contains a message text, and <code>Reactions</code>, which displays the available reactions. It also has a <code>Box</code> used as overlay/background, covering the whole screen. You can find the complete component from <a href="https://github.com/eevajonnapanula/CatGPT/blob/main/app/src/main/java/com/eevajonna/catgpt/ui/components/ReactionPicker.kt"><code>ReactionPicker.kt</code></a></p>
<p>Okay, now we have the component, which takes the y-position of the message as a parameter. Let's move on to placing the elements.</p>
<h3 id="placing-elements-on-the-screen">Placing Elements on the Screen</h3>
<p>To place the message block and reactions correctly within the screen, we use a custom modifier that takes the y-position of the message from the message screen and finds the correct position for the message and reactions:</p>
<pre><code class="language-kotlin"><span class="token keyword">fun</span> Modifier<span class="token punctuation">.</span><span class="token function">positionReactionsPicker</span><span class="token punctuation">(</span>yPosition<span class="token operator">:</span> Dp<span class="token punctuation">)</span> <span class="token operator">=</span> layout <span class="token punctuation">{</span> measurable<span class="token punctuation">,</span> constraints <span class="token operator">-></span>
<span class="token keyword">val</span> placeable <span class="token operator">=</span> measurable<span class="token punctuation">.</span><span class="token function">measure</span><span class="token punctuation">(</span>constraints<span class="token punctuation">)</span>
<span class="token function">layout</span><span class="token punctuation">(</span>placeable<span class="token punctuation">.</span>width<span class="token punctuation">,</span> placeable<span class="token punctuation">.</span>height<span class="token punctuation">)</span> <span class="token punctuation">{</span>
placeable<span class="token punctuation">.</span><span class="token function">place</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span> yPosition<span class="token punctuation">.</span><span class="token function">roundToPx</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>To accomplish what we want, we use the <code>layout</code>-modifier. It's a lambda with two parameters: <code>measurable</code> and <code>constraints.</code> We first measure the element presented by the <code>measurable</code> parameter (the component on which the custom modifier is called) and store the value in the <code>placeable</code> variable. </p>
<p>Then, we create the layout with measured height and width and place the element with <code>placeable.place()</code>. For the x-value, we use "0" because the component fills the whole width, and we can place it on the left edge of the screen. For the y-value, we use the <code>yPosition</code> passed to the modifier and round it to pixels. </p>
<p>Finally, we call the custom modifier on the <code>ConstraintLayout</code> that is wrapping the message and reactions:</p>
<pre><code class="language-kotlin"><span class="token function">ConstraintLayout</span><span class="token punctuation">(</span>
modifier <span class="token operator">=</span> Modifier<span class="token punctuation">.</span><span class="token function">positionReactionsPicker</span><span class="token punctuation">(</span>yPosition<span class="token punctuation">)</span>
<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token operator">..</span><span class="token punctuation">.</span> <span class="token punctuation">}</span>
</code></pre><p>In this way, we have positioned the reactions picker: </p>
<img src="https://images.ctfassets.net/mpqufjsy02zr/2son0NyrlhPY8OlBBeKBMa/02bc563227cbf2597fd9c637f58c6da3/Screenshot_20231020_071910.png" alt="CatGPT-app with a message consisting of multiple 'meow's open. Above the message, there are four cat emojis: Grinning cat, grinning cat with smiling eyes, weary cat and smiling cat with heart eyes." class="portrait-img" />
<h2 id="wrapping-up">Wrapping Up</h2>
<p>In this blog post, we've looked at how to position elements on screen. We've done that by getting the y-position of that message and then creating an overlay where we've placed the opened message with the help of a custom layout modifier. </p>
<p>This code is a good starting place visually but has some accessibility problems. Not everyone who uses different assistive technologies can use it, so I will write a second blog post tackling some of the accessibility issues this code has.</p>
<p>Do you have any comments or questions? Please share and/or ask!</p>
</article>
How to Add Content Descriptions in Compose - A Guide for Android Devs2023-11-15T00:00:00.000Zhttps://eevis.codes/blog/2023-11-15/how-to-add-content-descriptions-in-compose-a-guide-for-android-devs/
<nav id="breadcrumbs" aria-label="breadcrumbs">
<ol>
<li>
<a href="https://eevis.codes/blog/">Blog</a>
<span aria-hidden="true" class="breadcrumb-separator">/<span>
</span></span></li>
<li>How to Add Content Descriptions in Compose - A Guide for Android Devs</li><li>
</li></ol>
</nav>
<article class="blogPostLong">
<h1 id="/2023-11-15/how-to-add-content-descriptions-in-compose-a-guide-for-android-devs">How to Add Content Descriptions in Compose - A Guide for Android Devs</h1>
<div class="reading-time-and-date">
<p>
Reading time: about 7 minutes
</p>
<span class="separator"></span>
<p>Published <time datetime="15th Nov, 2023">15th Nov, 2023</time></p>
</div>
<section class="likes-and-boosts">
<div>
<svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="boosts-title">
<title id="boosts-title">Boosts</title>
<path d="M7.19 4.35047C5.15 6.64047 3.75 9.93047 3.62 10.2405L0 8.69047L4.05 4.64047C4.52 4.17047 5.2 3.96047 5.86 4.09047L7.19 4.35047ZM9.17 15.0005C9.17 15.0005 12.91 13.4505 15.06 11.3005C20.46 5.90047 19.56 1.68047 19.27 0.730473C18.32 0.430473 14.1 -0.459527 8.7 4.94047C6.55 7.09047 5 10.8305 5 10.8305L9.17 15.0005ZM15.65 12.8105C13.36 14.8505 10.07 16.2505 9.76 16.3805L11.31 20.0005L15.36 15.9505C15.83 15.4805 16.04 14.8005 15.91 14.1405L15.65 12.8105ZM7 16.0005C7 16.8305 6.66 17.5805 6.12 18.1205C4.94 19.3005 0 20.0005 0 20.0005C0 20.0005 0.7 15.0605 1.88 13.8805C2.2988 13.4595 2.83325 13.1724 3.4155 13.0558C3.99775 12.9392 4.60153 12.9982 5.15017 13.2253C5.69882 13.4525 6.16758 13.8376 6.49694 14.3317C6.8263 14.8258 7.0014 15.4067 7 16.0005ZM11 7.00047C11 5.90047 11.9 5.00047 13 5.00047C14.1 5.00047 15 5.90047 15 7.00047C15 8.10047 14.1 9.00047 13 9.00047C11.9 9.00047 11 8.10047 11 7.00047Z" fill="#00696D"></path>
</svg>
0
</div>
<div>
<svg role="img" width="20" height="19" viewBox="0 0 20 19" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="likes-title">
<title id="likes-title">Likes</title>
<path d="M10 18.35L8.55 17.03C3.4 12.36 0 9.27 0 5.5C0 2.41 2.42 0 5.5 0C7.24 0 8.91 0.81 10 2.08C11.09 0.81 12.76 0 14.5 0C17.58 0 20 2.41 20 5.5C20 9.27 16.6 12.36 11.45 17.03L10 18.35Z"></path>
</svg>
0
</div>
</section>
<img class="cover-photo" src="https://images.ctfassets.net/mpqufjsy02zr/5aTib3hHBr7scU4fOFnrIR/d1d74df1017b61cfa3cfd71c6e44e123/content_desc__3_.png" alt="How to Add Content Descriptions in Compose - A Guide for Android Devs" />
<div class="blog-tags">
<a href="https://eevis.codes/tags/Kotlin"><span class="blog-tag turquoise">Kotlin</span></a> <a href="https://eevis.codes/tags/Android"><span class="blog-tag blue">Android</span></a>
</div>
<nav aria-label="How to Add Content Descriptions in Compose - A Guide for Android Devs Table of Contents" class="table-of-contents">
<h2>Table of Contents</h2>
<ol>
<li><a href="https://eevis.codes/blog/2023-11-15/how-to-add-content-descriptions-in-compose-a-guide-for-android-devs/#what-content-description-is"> What Content Description Is</a></li><li><a href="https://eevis.codes/blog/2023-11-15/how-to-add-content-descriptions-in-compose-a-guide-for-android-devs/#content-description-is-not-available-for-all-users"> Content Description is not Available for All Users</a></li><li><a href="https://eevis.codes/blog/2023-11-15/how-to-add-content-descriptions-in-compose-a-guide-for-android-devs/#how-to-write-a-good-content-description"> How to Write a Good Content Description</a></li><li><a href="https://eevis.codes/blog/2023-11-15/how-to-add-content-descriptions-in-compose-a-guide-for-android-devs/#how-to-set-the-content-description"> How to Set the Content Description</a></li><li><a href="https://eevis.codes/blog/2023-11-15/how-to-add-content-descriptions-in-compose-a-guide-for-android-devs/#wrapping-up"> Wrapping Up</a></li><li><a href="https://eevis.codes/blog/2023-11-15/how-to-add-content-descriptions-in-compose-a-guide-for-android-devs/#links-in-blog-post"> Links in Blog Post</a></li>
</ol>
</nav>
<p>When it comes to the <code>contentDescription</code>-attribute, I've noticed a couple of things Android devs do that are wrong and make apps harder to use with assistive technology. The first thing I see a lot is setting the content description of an icon to the icon name, such as <code>icon_cat_sleeping.</code> Another thing is suppressing the warnings Android Studio gives about missing content descriptions. </p>
<p>I decided to write about this theme, and in this blog post, you'll learn about content descriptions: the problems and solutions. And yes, you'll understand why those things I mentioned in the previous paragraph are problematic.</p>
<p>Let's start discussing content description, then continue with how to write one, and finally, how to set the content description.</p>
<h2 id="what-content-description-is">What Content Description Is</h2>
<p>When I talk about content description, I mean text alternative. If you have any background in web development or, e.g., using any social media platforms, you might have heard about the word "alt text". They mean the same thing and different environments have different names for this concept. </p>
<p>In Android, the attribute is called <code>contentDescription,</code> and I think that content description is actually a very explanatory name for what we're trying to accomplish with it: describing the content of an image or a graphic. </p>
<p>One thing that's important to remember is who we are describing the content for: Users who utilize the accessibility APIs. This means, for example, screen reader and voice access users. Screen reader users rely on the content descriptions for visual content (that they usually can't see, depending on their sight level), and voice access users can use these values as labels to interact with elements, such as buttons. </p>
<p>Content descriptions should be added for meaningful visual elements like images, icons, and graphs. For some of the elements, it's more straightforward, but for, for example, different types of graphs, it's more complicated. This blog post won't go into details on adding content descriptions for graphs, but I've written a blog post as one example around the theme if you're interested: <a href="https://eevis.codes/blog/2023-07-24/more-accessible-graphs-with-jetpack-compose-part-1-adding-content/">More Accessible Graphs with Jetpack Compose Part 1: Adding Content Description</a>. </p>
<p>Now, if you've been working with testing tools such as Appium, you might remember adding the test id as <code>contentDescription.</code> Unfortunately, some tools use the so-called "accessibility id," which maps to <code>contentDescription</code> on Android. On iOS, there is a separate property called <code>accessibilityId</code>. This is a problem because content description actually overrides the text value for many elements, meaning that for these elements, a screen reader user would hear the value of a button being <code>button_submit</code> (the id set for testing) instead of the "Submit" that's a text for that button.</p>
<p>I've had to fix issues like this, and my solution was to check that if a screen reader is running, we would set the element's content as <code>contentDescription</code>; otherwise, use that testing id. That is not an optimal solution, but it was the best we could do at the time. And this would fail with some of the automated accessibility checks, even if it works for real screen reader users.</p>
<h2 id="content-description-is-not-available-for-all-users">Content Description is not Available for All Users</h2>
<p>The content description is invisible to most users. This means that it's not the all-powerful solution to fix accessibility issues. </p>
<p>A common misconception is that adding it as a label to an icon would make it available to every user. But that's not the case - a user who is not using any sort of assistive technology can't see it, nor can someone with, for example, a keyboard. So, it's available just for assistive technologies that utilize the accessibility API underneath - including, but not limited to, screen readers, switch access, and voice navigation.</p>
<p>I want to underline this because many users usually would benefit from things like text labels with icons, but they tend to be dismissed when asking for them because of the abovementioned misconception. I'm one of them, so I've experienced this firsthand. </p>
<h2 id="how-to-write-a-good-content-description">How to Write a Good Content Description</h2>
<p>The actual content for the <code>contentDescription</code> attribute varies, depending on the purpose and type of the element. As mentioned, more complex visualizations are outside the scope of this blog post, so we will discuss meaningful and decorative images. We'll also discuss icons as a separate, unique use case. </p>
<p>One important thing to note is that whenever you write a content description, remember to localize it! </p>
<h3 id="meaningful-images">Meaningful Images</h3>
<p>What does a "meaningful image" mean? It means any image (or graphic) that has meaning and adds something to the content. It includes icons, and pictures that contain instructions is another good example. The text alternative (so, content description) should contain what the image is trying to communicate. </p>
<p>An image's content description is also affected by the context where the image is. WebAIM has a list of instructions for writing text alternatives:</p>
<blockquote>
<p>The alt attribute should <strong>typically</strong>:</p>
<ul>
<li><strong>be accurate and equivalent</strong> in representing content and function.</li>
<li><strong>be succinct</strong>. Content (if any) and function (if any) should be presented as succinctly as possible, without sacrificing accuracy. Typically, only a few words are necessary, though rarely a short sentence or two may be appropriate.</li>
<li><strong>not be redundant</strong> or provide the same information as text near the image.</li>
<li><strong>not include phrases like "image of ..." or "graphic of ...", etc.</strong> This would be redundant since screen readers already announce "graphic" along with the alt text. If the fact that an image is a photograph or illustration, etc. is important content, it may be useful to include this in alternative text.</li>
</ul>
</blockquote>
<p>Source and read more: <a href="https://webaim.org/techniques/alttext/">WebAIM: Alternative text</a></p>
<h3 id="decorative-images">Decorative Images</h3>
<p>In the previous section, we've talked about meaningful images. Sometimes, illustrations or images are there for purely decorative purposes and don't have any other meaning. In these cases, you should mark the content description as null:</p>
<pre><code class="language-kotlin"><span class="token function">Image</span><span class="token punctuation">(</span>
<span class="token operator">..</span><span class="token punctuation">.</span>
contentDescription<span class="token operator">=</span><span class="token keyword">null</span>
<span class="token punctuation">)</span>
</code></pre><p>Now, you might wonder, why not leave it as an empty string? The reason is that when left empty, the accessibility API doesn't get the info that this is decorative and keeps this image in the accessibility tree. This leads to a situation where, when a user encounters that image, they would hear "Unnamed" instead of not encountering it in the first place. </p>
<p>That "Unnamed" is redundant information and can be really confusing. Is that image really decorative, and a developer didn't know how to set it as such? Or is it meaningful, but the developer forgot to add the actual text alternative? </p>
<p>In the Compose world, components like <code>Image</code> and <code>Icon</code> require <code>contentDescription</code> to be set explicitly. However, it was (or, is) possible to leave the attribute out in the View world and XML-layouts. </p>
<p>That would lead to similar situations, with "Unnamed" being announced. This would also create a warning in the Android Studio about missing content descriptions. It's possible to suppress that warning, and I've fixed multiple instances where the warning was suppressed. </p>
<p>So, don't suppress that warning - set the content description to <code>null</code> or provide a descriptive content description, depending on the image.</p>
<h3 id="icons">Icons</h3>
<p>As mentioned, icons are a special case. Icons are often used as the only action indicator (like an edit button with just some icon representing editing). I usually have problems with this because of how I process information. I keep asking for text labels but typically don't get them. But this is not a blog post about text labels; it's about content descriptions, so let's move on.</p>
<p>When an icon is the only thing inside a button, it's even more critical to include meaningful content descriptions because it's the text used for the button. So, when a screen reader user encounters a button with an icon that is a quill, they'd hear something like "Button, Edit" instead of "Button, icon underscore quill" if the content description is set to the icon's name. Or, in the case I mentioned in the intro, "Icon underscore cat underscore sleeping." Remember that whatever text you enter there will be what non-sighted screen reader users hear (or read from braille) - they can't see the icon.</p>
<p>So, when adding a content description for an icon, ask yourself if it's used to convey meaning or if it's purely decorative. If it's the latter, use <code>null</code>. Otherwise, describe the action or meaning, not the actual icon. So, usually, an "X" icon represents something like "Close," and a chevron pointing to the left means something like "Back". </p>
<h2 id="how-to-set-the-content-description">How to Set the Content Description</h2>
<h3 id="contentdescription-attribute"><code>contentDescription</code>-attribute</h3>
<p>You can set the content description through the <code>contentDescription</code>-attribute for <code>Image</code> and <code>Icon</code> -components. Other components don't have this attribute, so you'll need the help of <code>semantics</code> or <code>clearAndSetSemantics</code>-modifiers. </p>
<h3 id="semantics-and-clearandsetsemantics-modifiers"><code>semantics</code> and <code>clearAndSetSemantics</code>-modifiers</h3>
<p>Both <code>semantics</code> and <code>clearAndSetSemantics</code> modifiers change the element's semantics. The difference is that the first builds on top of existing semantics, and the latter clears all the other semantics.</p>
<p>While <code>Image</code> and <code>Icon</code> components have the <code>contentDescription,</code> other elements, such as graphics created with <code>Canvas,</code> would require using these modifiers. Also another case is text with an emoji - for example, if your app is a chat app with reactions, and you're using <code>Text</code>-element for displaying the reaction, you'd need to set the content description for those <code>Text</code> components. </p>
<p>You can use the <code>semantics</code>-modifier in any element and set the content description within its semantic properties lambda:</p>
<pre><code class="language-kotlin"><span class="token function">Canvas</span><span class="token punctuation">(</span>
modifier <span class="token operator">=</span> Modifier<span class="token punctuation">.</span><span class="token function">semantics</span> <span class="token punctuation">{</span>
contentDescription <span class="token operator">=</span> <span class="token string-literal singleline"><span class="token string">"Put the content description here"</span></span>
<span class="token punctuation">}</span>
<span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token operator">..</span><span class="token punctuation">.</span>
<span class="token punctuation">}</span>
</code></pre><h2 id="wrapping-up">Wrapping Up</h2>
<p>In this blog post, we've covered content descriptions, what they are, how to write them, and how you can set them. </p>
<p>Do you have questions? Or comments? Please do ask or share!</p>
<h2 id="links-in-blog-post">Links in Blog Post</h2>
<ul>
<li><a href="https://eevis.codes/blog/2023-07-24/more-accessible-graphs-with-jetpack-compose-part-1-adding-content/">More Accessible Graphs with Jetpack Compose Part 1: Adding Content Description</a></li>
<li><a href="https://webaim.org/techniques/alttext/">WebAIM: Alternative text</a></li>
</ul>
</article>
Women Developer Academy Europe 2023 - My Experience2023-11-24T00:00:00.000Zhttps://eevis.codes/blog/2023-11-24/women-developer-academy-europe-2023-my-experience/
<nav id="breadcrumbs" aria-label="breadcrumbs">
<ol>
<li>
<a href="https://eevis.codes/blog/">Blog</a>
<span aria-hidden="true" class="breadcrumb-separator">/<span>
</span></span></li>
<li>Women Developer Academy Europe 2023 - My Experience</li><li>
</li></ol>
</nav>
<article class="blogPostLong">
<h1 id="/2023-11-24/women-developer-academy-europe-2023-my-experience">Women Developer Academy Europe 2023 - My Experience</h1>
<div class="reading-time-and-date">
<p>
Reading time: about 4 minutes
</p>
<span class="separator"></span>
<p>Published <time datetime="24th Nov, 2023">24th Nov, 2023</time></p>
</div>
<section class="likes-and-boosts">
<div>
<svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="boosts-title">
<title id="boosts-title">Boosts</title>
<path d="M7.19 4.35047C5.15 6.64047 3.75 9.93047 3.62 10.2405L0 8.69047L4.05 4.64047C4.52 4.17047 5.2 3.96047 5.86 4.09047L7.19 4.35047ZM9.17 15.0005C9.17 15.0005 12.91 13.4505 15.06 11.3005C20.46 5.90047 19.56 1.68047 19.27 0.730473C18.32 0.430473 14.1 -0.459527 8.7 4.94047C6.55 7.09047 5 10.8305 5 10.8305L9.17 15.0005ZM15.65 12.8105C13.36 14.8505 10.07 16.2505 9.76 16.3805L11.31 20.0005L15.36 15.9505C15.83 15.4805 16.04 14.8005 15.91 14.1405L15.65 12.8105ZM7 16.0005C7 16.8305 6.66 17.5805 6.12 18.1205C4.94 19.3005 0 20.0005 0 20.0005C0 20.0005 0.7 15.0605 1.88 13.8805C2.2988 13.4595 2.83325 13.1724 3.4155 13.0558C3.99775 12.9392 4.60153 12.9982 5.15017 13.2253C5.69882 13.4525 6.16758 13.8376 6.49694 14.3317C6.8263 14.8258 7.0014 15.4067 7 16.0005ZM11 7.00047C11 5.90047 11.9 5.00047 13 5.00047C14.1 5.00047 15 5.90047 15 7.00047C15 8.10047 14.1 9.00047 13 9.00047C11.9 9.00047 11 8.10047 11 7.00047Z" fill="#00696D"></path>
</svg>
0
</div>
<div>
<svg role="img" width="20" height="19" viewBox="0 0 20 19" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="likes-title">
<title id="likes-title">Likes</title>
<path d="M10 18.35L8.55 17.03C3.4 12.36 0 9.27 0 5.5C0 2.41 2.42 0 5.5 0C7.24 0 8.91 0.81 10 2.08C11.09 0.81 12.76 0 14.5 0C17.58 0 20 2.41 20 5.5C20 9.27 16.6 12.36 11.45 17.03L10 18.35Z"></path>
</svg>
0
</div>
</section>
<img class="cover-photo" src="https://images.ctfassets.net/mpqufjsy02zr/73mAAQQvnfBULhthuQPti0/e78d63361fe8a38104ff4faf1d24558e/WDA.png" alt="Women Developer Academy Europe 2023 - My Experience" />
<div class="blog-tags">
<a href="https://eevis.codes/tags/career"><span class="blog-tag purple">career</span></a>
</div>
<nav aria-label="Women Developer Academy Europe 2023 - My Experience Table of Contents" class="table-of-contents">
<h2>Table of Contents</h2>
<ol>
<li><a href="https://eevis.codes/blog/2023-11-24/women-developer-academy-europe-2023-my-experience/#women-developer-academy-wda"> Women Developer Academy (WDA)</a></li><li><a href="https://eevis.codes/blog/2023-11-24/women-developer-academy-europe-2023-my-experience/#sessions"> Sessions</a></li><li><a href="https://eevis.codes/blog/2023-11-24/women-developer-academy-europe-2023-my-experience/#i-am-remarkable-workshop"> I Am Remarkable-workshop</a></li><li><a href="https://eevis.codes/blog/2023-11-24/women-developer-academy-europe-2023-my-experience/#mentoring"> Mentoring</a></li><li><a href="https://eevis.codes/blog/2023-11-24/women-developer-academy-europe-2023-my-experience/#mini-conference"> Mini-Conference</a></li><li><a href="https://eevis.codes/blog/2023-11-24/women-developer-academy-europe-2023-my-experience/#final-thoughts"> Final Thoughts</a></li><li><a href="https://eevis.codes/blog/2023-11-24/women-developer-academy-europe-2023-my-experience/#links-in-blog-post"> Links in Blog Post</a></li>
</ol>
</nav>
<p>My October was busy. I spoke at three conferences (droidcon Italy, droidcon London, and KKON, which was an online conference). I had also applied to the Women Developer Academy Europe earlier, and when the acceptance letter arrived at the end of September, I wasn't sure if I could participate. But as you might guess from me writing this blog post, I decided to attend, and it was a great decision! In this blog post, I'll share my experiences with the program. </p>
<h2 id="women-developer-academy-wda">Women Developer Academy (WDA)</h2>
<p>Women Developer Academy is a program from Google, and <a href="https://rsvp.withgoogle.com/events/women-developer-academy-europe/">the event page for our cohort (Women Developer Academy Europe)</a> explains it in a nutshell:</p>
<blockquote>
<p>The Women Developer Academy is a specialized program for female developers, aimed at increasing the diversity of speakers at tech events in the region, focusing on creating an inclusive community of speakers and mentors who can support each other. </p>
</blockquote>
<p>The program consisted of weekly sessions, the I Am Remarkable workshop, mentoring, and a mini-conference with 5-minute talks. I'll share a bit more about them next.</p>
<h2 id="sessions">Sessions</h2>
<p>The program had 2-3 evening sessions each week. The topics for the meetings were about public speaking, applying to conferences, online branding, and many other related topics. Many of those topics were familiar, as I've spoken at many conferences, but I also learned new things.</p>
<p>For me, living in the eastern side of Europe in Finland, these meetings started at seven in the evening. It's not an optimal time for someone who is definitely a morning person, and needs their wind-down time in the evening. But I was so happy that we got the sessions as recordings and could watch them later! </p>
<h2 id="i-am-remarkable-workshop">I Am Remarkable-workshop</h2>
<p>The WDA program included a <a href="https://rmrkblty.org/">I Am Remarkable workshop</a>. The website describes the movement behind this workshop:</p>
<blockquote>
<p>#IAmRemarkable is a global movement that empowers everyone to celebrate their achievements in the workplace and beyond.</p>
</blockquote>
<p>I've heard of this workshop but never got to attend one. I can see the need for such workshops - especially within minority groups in tech. It's so easy to forget or not see the accomplishments, and this workshop is a great time to dedicate some time to acknowledging and celebrating them.</p>
<p>We spent one Saturday afternoon talking about ways we are remarkable, and how self-promotion matters. I can share a couple of sentences I wrote during the workshop:</p>
<p>I am remarkable because I care and know a lot about accessibility.</p>
<p>I am remarkable because I dare to question inequality.</p>
<p>I am remarkable because I'm really good at what I do. </p>
<h2 id="mentoring">Mentoring</h2>
<p>The third part of the program was mentoring. The minimum number of mentoring sessions to graduate from the program was 4. To be honest, I didn't have time to even think of having more mentoring sessions. I wish I had more time, but it is what it is.</p>
<p>I had four interesting mentoring sessions, and we discussed many different things related to accessibility, speaking at conferences, and the GDE program, to name a few. A huge thanks to Piotr Prus, Sabrina Jodexnis, Nourhan Gehad, and Carlos Mota!</p>
<h2 id="mini-conference">Mini-Conference</h2>
<p>In the last session before graduation from the program, we had a mini-conference where everyone could talk for 5 minutes about a topic they're interested in. The timing wasn't optimal for me - I had a flight to London the next day, but luckily, I got to present my talk second in order. </p>
<p>I spoke about Android developers and the challenges we face regarding accessibility. The talk was based on a blog post I've written: <a href="https://eevis.codes/blog/2023-07-17/android-developers-and-accessibility-challenges-and-proposed-solutions/">Android Developers and Accessibility - Challenges and Proposed Solutions</a>. Five minutes is a really short time, and the amount of content was almost too much for that time. I had to skip introductions to fit everything in.</p>
<p>I got good feedback, but it was interesting that some people said they liked my slides that had almost nothing on them - and some said that I should add, e.g., pictures. It is an excellent example of how we all are different - some like more simplistic slides, and others want more visual ones.</p>
<h2 id="final-thoughts">Final Thoughts</h2>
<p>Despite having a really busy October, I am happy I got accepted into WDA and decided to participate. I met amazing people, made connections, and learned new things. </p>
<p>So, if you are in the target group of this program, and whenever it opens in your area, I highly recommend applying!</p>
<h2 id="links-in-blog-post">Links in Blog Post</h2>
<ul>
<li><a href="https://rsvp.withgoogle.com/events/women-developer-academy-europe/">The event page for our cohort (Women Developer Academy Europe)</a></li>
<li><a href="https://rmrkblty.org/">I Am Remarkable workshop</a></li>
<li><a href="https://eevis.codes/blog/2023-07-17/android-developers-and-accessibility-challenges-and-proposed-solutions/">Android Developers and Accessibility - Challenges and Proposed Solutions</a></li>
</ul>
</article>
More Accessible Graphs with Jetpack Compose Part 4: On-Screen Control Buttons2023-12-09T00:00:00.000Zhttps://eevis.codes/blog/2023-12-09/more-accessible-graphs-with-jetpack-compose-part-4-on-screen-control-buttons/
<nav id="breadcrumbs" aria-label="breadcrumbs">
<ol>
<li>
<a href="https://eevis.codes/blog/">Blog</a>
<span aria-hidden="true" class="breadcrumb-separator">/<span>
</span></span></li>
<li>More Accessible Graphs with Jetpack Compose Part 4: On-Screen Control Buttons</li><li>
</li></ol>
</nav>
<article class="blogPostLong">
<h1 id="/2023-12-09/more-accessible-graphs-with-jetpack-compose-part-4-on-screen-control-buttons">More Accessible Graphs with Jetpack Compose Part 4: On-Screen Control Buttons</h1>
<div class="reading-time-and-date">
<p>
Reading time: about 8 minutes
</p>
<span class="separator"></span>
<p>Published <time datetime="9th Dec, 2023">9th Dec, 2023</time></p>
</div>
<section class="likes-and-boosts">
<div>
<svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="boosts-title">
<title id="boosts-title">Boosts</title>
<path d="M7.19 4.35047C5.15 6.64047 3.75 9.93047 3.62 10.2405L0 8.69047L4.05 4.64047C4.52 4.17047 5.2 3.96047 5.86 4.09047L7.19 4.35047ZM9.17 15.0005C9.17 15.0005 12.91 13.4505 15.06 11.3005C20.46 5.90047 19.56 1.68047 19.27 0.730473C18.32 0.430473 14.1 -0.459527 8.7 4.94047C6.55 7.09047 5 10.8305 5 10.8305L9.17 15.0005ZM15.65 12.8105C13.36 14.8505 10.07 16.2505 9.76 16.3805L11.31 20.0005L15.36 15.9505C15.83 15.4805 16.04 14.8005 15.91 14.1405L15.65 12.8105ZM7 16.0005C7 16.8305 6.66 17.5805 6.12 18.1205C4.94 19.3005 0 20.0005 0 20.0005C0 20.0005 0.7 15.0605 1.88 13.8805C2.2988 13.4595 2.83325 13.1724 3.4155 13.0558C3.99775 12.9392 4.60153 12.9982 5.15017 13.2253C5.69882 13.4525 6.16758 13.8376 6.49694 14.3317C6.8263 14.8258 7.0014 15.4067 7 16.0005ZM11 7.00047C11 5.90047 11.9 5.00047 13 5.00047C14.1 5.00047 15 5.90047 15 7.00047C15 8.10047 14.1 9.00047 13 9.00047C11.9 9.00047 11 8.10047 11 7.00047Z" fill="#00696D"></path>
</svg>
0
</div>
<div>
<svg role="img" width="20" height="19" viewBox="0 0 20 19" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="likes-title">
<title id="likes-title">Likes</title>
<path d="M10 18.35L8.55 17.03C3.4 12.36 0 9.27 0 5.5C0 2.41 2.42 0 5.5 0C7.24 0 8.91 0.81 10 2.08C11.09 0.81 12.76 0 14.5 0C17.58 0 20 2.41 20 5.5C20 9.27 16.6 12.36 11.45 17.03L10 18.35Z"></path>
</svg>
0
</div>
</section>
<img class="cover-photo" src="https://images.ctfassets.net/mpqufjsy02zr/bnonU2dSCPHLgUev8MgMK/be2f085ea6f3dc01ab7ea802255a979e/more-accessible-graphs-4__1_.png" alt="More Accessible Graphs with Jetpack Compose Part 4: On Screen Control Buttons" />
<div class="blog-tags">
<a href="https://eevis.codes/tags/a11y"><span class="blog-tag turquoise">a11y</span></a> <a href="https://eevis.codes/tags/Kotlin"><span class="blog-tag turquoise">Kotlin</span></a> <a href="https://eevis.codes/tags/Android"><span class="blog-tag blue">Android</span></a>
</div>
<nav aria-label="More Accessible Graphs with Jetpack Compose Part 4: On-Screen Control Buttons Table of Contents" class="table-of-contents">
<h2>Table of Contents</h2>
<ol>
<li><a href="https://eevis.codes/blog/2023-12-09/more-accessible-graphs-with-jetpack-compose-part-4-on-screen-control-buttons/#adding-buttons"> Adding Buttons</a></li><li><a href="https://eevis.codes/blog/2023-12-09/more-accessible-graphs-with-jetpack-compose-part-4-on-screen-control-buttons/#switch-access"> Switch Access</a></li><li><a href="https://eevis.codes/blog/2023-12-09/more-accessible-graphs-with-jetpack-compose-part-4-on-screen-control-buttons/#considerations"> Considerations</a></li><li><a href="https://eevis.codes/blog/2023-12-09/more-accessible-graphs-with-jetpack-compose-part-4-on-screen-control-buttons/#wrapping-up"> Wrapping Up</a></li><li><a href="https://eevis.codes/blog/2023-12-09/more-accessible-graphs-with-jetpack-compose-part-4-on-screen-control-buttons/#links-in-blog-post"> Links in Blog Post</a></li>
</ol>
</nav>
<p>This blog post is the fourth one in my series on more accessible graphs with Jetpack Compose. You can find the previous three from the following links:</p>
<ul>
<li><a href="https://eevis.codes/blog/2023-07-24/more-accessible-graphs-with-jetpack-compose-part-1-adding-content/">More Accessible Graphs with Jetpack Compose Part 1: Adding Content Description</a></li>
<li><a href="https://eevis.codes/blog/2023-07-31/more-accessible-graphs-with-jetpack-compose-part-2-adding-keyboard-interaction/">More Accessible Graphs with Jetpack Compose Part 2: Adding Keyboard Interaction</a></li>
<li><a href="https://eevis.codes/blog/2023-08-16/more-accessible-graphs-with-jetpack-compose-part-3-differentiating-without/">More Accessible Graphs with Jetpack Compose Part 3: Differentiating without Color</a></li>
</ul>
<p>Continuous (or path-based) pointer input, like drag-gesture, might be problematic for some users. For example, if a user has tremors in their hands, the gesture is not always continuous, and if the app relies on this kind of gesture to work, it may be unusable for these users.</p>
<p>Now, if you have followed this blog post series, you might wonder - hey, we added keyboard interaction; isn't that enough? No, it's not - it would require a physical keyboard. Many users don't use a physical keyboard, even if they could benefit from it. </p>
<p>So, in this blog post, we'll add on-screen controls to the graph. The best part is that this solution also solves the issues with a switch device I mentioned at the end of the blog post about keyboard interaction! Head to the paragraph about <a href="https://eevis.codes/blog/2023-12-09/more-accessible-graphs-with-jetpack-compose-part-4-on-screen-control-buttons/#switch-access">Switch Access</a> for more in-depth explanations. </p>
<h2 id="adding-buttons">Adding Buttons</h2>
<p>So, one way to solve this problem with path-based gestures is to add visible buttons as an alternative way of navigating inside the graph. In the case of this graph, we want to add two buttons: One for going forward and another for going backward. This is what the UI will look like once we add the controls:</p>
<img src="https://images.ctfassets.net/mpqufjsy02zr/2MilAAL2neGpVhLZ1Ok8c8/021955c6ac95a3a0083532bfe2dbb86b/Screenshot_20231208_071934.png" alt="Graph example app's UI, with newly added Previous year and Next year-buttons under the graph." class="portrait-img" />
<p>We add a new component, called <code>ControlButtons</code> that wraps these buttons: </p>
<pre><code class="language-kotlin"><span class="token annotation builtin">@Composable</span>
<span class="token keyword">fun</span> <span class="token function">ControlButtons</span><span class="token punctuation">(</span>
highlightedX<span class="token operator">:</span> Float<span class="token operator">?</span><span class="token punctuation">,</span>
lastIndex<span class="token operator">:</span> Int<span class="token punctuation">,</span>
setFocus<span class="token operator">:</span> <span class="token punctuation">(</span>Int<span class="token punctuation">)</span> <span class="token operator">-></span> Unit
<span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token operator">..</span><span class="token punctuation">.</span>
<span class="token punctuation">}</span>
</code></pre><p>It takes three parameters: <code>highlightedX</code>, which is the currently highlighted year on the x-axis, <code>lastIndex</code>, which is the last year's index, and a function called <code>setFocus</code>, which takes in an integer - the index where the focus should go next - and returns <code>Unit</code>. This is what it looks like when we call it in the parent component:</p>
<pre><code class="language-kotlin"><span class="token function">ControlButtons</span><span class="token punctuation">(</span>
highlightedX <span class="token operator">=</span> highlightedX<span class="token punctuation">,</span>
lastIndex <span class="token operator">=</span> pixelPointsForTotal<span class="token punctuation">.</span><span class="token function">count</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">-</span> <span class="token number">1</span><span class="token punctuation">,</span>
<span class="token punctuation">)</span> <span class="token punctuation">{</span> selectedIndex <span class="token operator">-></span>
highlightedX <span class="token operator">=</span> pixelPointsForTotal<span class="token punctuation">[</span>selectedIndex<span class="token punctuation">]</span><span class="token punctuation">.</span>x
<span class="token punctuation">}</span>
</code></pre><p>So, we pass in the <code>highlightedX</code> that we have been using to define the currently highlighted year, the last index for pixel point lists, and then, in the lambda block, we take in the new selected index and set the <code>highlightedX</code> to the x-value in the pixel point list in that index.</p>
<p>Let's get back to the <code>ControlButtons</code> composable. At the beginning of the composable, we'll define two things:</p>
<pre><code class="language-kotlin"><span class="token comment">// ControlButtons</span>
<span class="token keyword">var</span> selectedIndex <span class="token keyword">by</span> remember <span class="token punctuation">{</span>
<span class="token function">mutableStateOf</span><span class="token punctuation">(</span><span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token keyword">fun</span> <span class="token function">setSelectedIndexAndFocus</span><span class="token punctuation">(</span>newIndex<span class="token operator">:</span> Int<span class="token punctuation">)</span> <span class="token punctuation">{</span>
selectedIndex <span class="token operator">=</span> newIndex
<span class="token function">setFocus</span><span class="token punctuation">(</span>newIndex<span class="token punctuation">)</span>
<span class="token punctuation">}</span>
</code></pre><p>First, we want to store the currently selected index to a <code>selectedIndex</code> variable, remember it and set the initial value to -1 as there is no index selected initially. We'll also want a function to set a selected index and focus to update the state within the component, and set the new highlighted year (or, x) on the parent component, as we saw a couple of paragraphs ago. We call it <code>setSelectedIndexAndFocus</code>. </p>
<p>After that, we'll have everything we need in that component to create the actual buttons. As they're pretty similar, we'll make a new component called <code>ControlButton</code> that takes in type (we'll talk about it a bit later), currently selected index, last index, first focus index (so, where the focus should go first when pressing this button if nothing is selected), highlighted X value, and a function to set the focus:</p>
<pre><code class="language-kotlin"><span class="token annotation builtin">@Composable</span>
<span class="token keyword">fun</span> <span class="token function">ControlButton</span><span class="token punctuation">(</span>
type<span class="token operator">:</span> GraphControlType<span class="token punctuation">,</span>
currentIndex<span class="token operator">:</span> Int<span class="token punctuation">,</span>
lastIndex<span class="token operator">:</span> Int<span class="token punctuation">,</span>
firstFocusIndex<span class="token operator">:</span> Int<span class="token punctuation">,</span>
highlightedX<span class="token operator">:</span> Float<span class="token operator">?</span><span class="token punctuation">,</span>
setFocus<span class="token operator">:</span> <span class="token punctuation">(</span>Int<span class="token punctuation">)</span> <span class="token operator">-></span> Unit<span class="token punctuation">,</span>
<span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token operator">..</span><span class="token punctuation">.</span>
<span class="token punctuation">}</span>
</code></pre><p>To make the function a bit more abstract in order to work for both the next and previous years, I made an enum called <code>GraphControlType</code>: </p>
<pre><code class="language-kotlin"><span class="token keyword">enum</span> <span class="token keyword">class</span> GraphControlType <span class="token punctuation">{</span>
Next <span class="token punctuation">{</span>
<span class="token keyword">override</span> <span class="token keyword">val</span> icon <span class="token operator">=</span> Icons<span class="token punctuation">.</span>Filled<span class="token punctuation">.</span>ArrowForward
<span class="token keyword">override</span> <span class="token keyword">val</span> textResId <span class="token operator">=</span> R<span class="token punctuation">.</span>string<span class="token punctuation">.</span>button_next_year
<span class="token punctuation">}</span><span class="token punctuation">,</span>
Previous <span class="token punctuation">{</span>
<span class="token keyword">override</span> <span class="token keyword">val</span> icon <span class="token operator">=</span> Icons<span class="token punctuation">.</span>Filled<span class="token punctuation">.</span>ArrowBack
<span class="token keyword">override</span> <span class="token keyword">val</span> textResId <span class="token operator">=</span> R<span class="token punctuation">.</span>string<span class="token punctuation">.</span>button_previous_year
<span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">;</span>
<span class="token keyword">abstract</span> <span class="token keyword">val</span> icon<span class="token operator">:</span> ImageVector
<span class="token keyword">abstract</span> <span class="token keyword">val</span> textResId<span class="token operator">:</span> Int
<span class="token punctuation">}</span>
</code></pre><p>This enum contains the icon we want to use and the resource id for the text in the button. </p>
<p>As mentioned, the idea of the button is that it moves the focus to the next year when pressed. If it's the "next year" button, it would go forward from last year to the first one. If it's the "previous year" button, it would go backward; when it's in the first year, it would go to the last year.</p>
<p>We have helper functions for getting the next index (or, year) to focus (you can see them in the <a href="https://github.com/eevajonnapanula/graph-accessibility-example/commit/0b049a57c89a1fc73f0224c0f1e4130871f63ae9">commit containing all the changes</a>), and we want to define which one to use, depending on the type that we passed:</p>
<pre><code class="language-kotlin"><span class="token comment">// ControlButton</span>
<span class="token keyword">val</span> nextIndexFunc <span class="token operator">=</span> <span class="token keyword">when</span> <span class="token punctuation">(</span>type<span class="token punctuation">)</span> <span class="token punctuation">{</span>
GraphControlType<span class="token punctuation">.</span>Next <span class="token operator">-></span> <span class="token operator">::</span>getNextIndex
GraphControlType<span class="token punctuation">.</span>Previous <span class="token operator">-></span> <span class="token operator">::</span>getPreviousIndex
<span class="token punctuation">}</span>
</code></pre><p>Now that we've defined everything, we'll add the button:</p>
<pre><code class="language-kotlin"><span class="token comment">// ControlButton</span>
<span class="token function">OutlinedButton</span><span class="token punctuation">(</span>onClick <span class="token operator">=</span> <span class="token punctuation">{</span>
<span class="token keyword">val</span> newSelectedIndex <span class="token operator">=</span> <span class="token keyword">when</span> <span class="token punctuation">(</span>highlightedX<span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">null</span> <span class="token operator">-></span> firstFocusIndex
<span class="token keyword">else</span> <span class="token operator">-></span> <span class="token function">nextIndexFunc</span><span class="token punctuation">(</span>currentIndex<span class="token punctuation">,</span> lastIndex<span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token function">setFocus</span><span class="token punctuation">(</span>newSelectedIndex<span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>type <span class="token operator">==</span> GraphControlType<span class="token punctuation">.</span>Previous<span class="token punctuation">)</span>
<span class="token function">ControlButtonIcon</span><span class="token punctuation">(</span>icon <span class="token operator">=</span> type<span class="token punctuation">.</span>icon<span class="token punctuation">)</span>
<span class="token function">Text</span><span class="token punctuation">(</span><span class="token function">stringResource</span><span class="token punctuation">(</span>id <span class="token operator">=</span> type<span class="token punctuation">.</span>textResId<span class="token punctuation">)</span><span class="token punctuation">)</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>type <span class="token operator">==</span> GraphControlType<span class="token punctuation">.</span>Next<span class="token punctuation">)</span>
<span class="token function">ControlButtonIcon</span><span class="token punctuation">(</span>icon <span class="token operator">=</span> type<span class="token punctuation">.</span>icon<span class="token punctuation">)</span>
<span class="token punctuation">}</span>
</code></pre><p>So, in the <code>onClick</code> handler of the button, we check if the highlighted x-value is null, and if it is, then we move the focus to the first focus index, which is the first index for the "next year" button, and the last index for the "previous year" button. Otherwise, we'll use the defined <code>nextIndexFunc</code> to get the next index where the focus should go. Once we have the index, we call the <code>setFocus</code>-function with that index.</p>
<p>In the content of the button, we use the type to define the placement of the icon - for the "previous year" button, it's before the text, and for the "next year" button, it's after the text.</p>
<p>The final thing is to add the controls to the <code>ControlButtons</code>-composable. We want them to be on the same row, so we wrap them with the <code>Row</code>-component. I've left out some modifiers for clarity:</p>
<pre><code class="language-kotlin"><span class="token comment">// ControlButtons</span>
<span class="token function">Row</span><span class="token punctuation">(</span><span class="token operator">..</span><span class="token punctuation">.</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token function">ControlButton</span><span class="token punctuation">(</span>
type <span class="token operator">=</span> GraphControlType<span class="token punctuation">.</span>Previous<span class="token punctuation">,</span>
currentIndex <span class="token operator">=</span> selectedIndex<span class="token punctuation">,</span>
lastIndex <span class="token operator">=</span> lastIndex<span class="token punctuation">,</span>
firstFocusIndex <span class="token operator">=</span> lastIndex<span class="token punctuation">,</span>
highlightedX <span class="token operator">=</span> highlightedX<span class="token punctuation">,</span>
setFocus <span class="token operator">=</span> <span class="token punctuation">{</span>
<span class="token function">setSelectedIndexAndFocus</span><span class="token punctuation">(</span>it<span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">)</span>
<span class="token function">ControlButton</span><span class="token punctuation">(</span>
type <span class="token operator">=</span> GraphControlType<span class="token punctuation">.</span>Next<span class="token punctuation">,</span>
currentIndex <span class="token operator">=</span> selectedIndex<span class="token punctuation">,</span>
lastIndex <span class="token operator">=</span> lastIndex<span class="token punctuation">,</span>
firstFocusIndex <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">,</span>
highlightedX <span class="token operator">=</span> highlightedX<span class="token punctuation">,</span>
setFocus <span class="token operator">=</span> <span class="token punctuation">{</span>
<span class="token function">setSelectedIndexAndFocus</span><span class="token punctuation">(</span>it<span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">)</span>
<span class="token punctuation">}</span>
</code></pre><p>Okay, now we have added the controls. Here's a video of how they work:</p>
<video controls="" class="portrait-video">
<source src="https://videos.ctfassets.net/mpqufjsy02zr/4XpsFYQJKccoUFDRh8C1a1/927cecd6256905816651b3cd0e7d2561/Screen_recording_20231208_072026.mp4" type="video/mp4" />
</video>
<p><a href="https://github.com/eevajonnapanula/graph-accessibility-example/commit/0b049a57c89a1fc73f0224c0f1e4130871f63ae9">You can find the final code from this commit.</a></p>
<h2 id="switch-access">Switch Access</h2>
<p>The initial implementation for keyboard access did not work on a switch device. When I was testing it with the switch, I noticed that the focusable elements are not available to the API a switch device uses, so the implementation did not work. I don't know why this happens; I haven't had time to investigate it further, but if anyone knows more about the technical details behind this behavior, I'd be glad to learn more!</p>
<p>This is also an excellent example of why you must test with different assistive technologies. Assuming that something works without testing might lead to some inaccessible code. </p>
<p>So, you might wonder what the app now looks like for a switch device user. I recorded a short video: </p>
<video controls="" class="portrait-video">
<source src="https://videos.ctfassets.net/mpqufjsy02zr/L7625w3b2Ck4srPptAHFS/d64abb6fff34069a30700bc3d37aef71/Screen_recording_20231208_072144.mp4" type="video/mp4" />
</video>
<p>As you can see in the video, adding these buttons allows a switch device user to change the highlighted year and see the percentages for each year.</p>
<h2 id="considerations">Considerations</h2>
<p>This implementation has some considerations from the accessibility point of view. If I had started with these controls, I would probably have built the whole graph a bit differently from a screen-reader and keyboard user perspective, but this time, I was building on top of the previous blog posts, so that was a constraint. I'll share some concerns about this implementation and the reasoning behind my decisions. </p>
<h3 id="non-descriptive-buttons">Non-Descriptive Buttons</h3>
<p>First, the buttons in the UI have text content saying "Next year" and "Previous year," and when a screen-reader user presses those buttons, nothing happens, especially if they're non-sighted or have turned the screen off. This is because even though we move the currently selected year, we don't move the accessibility focus. Or focus at all, for that matter. </p>
<p>If I had added the button controls first, I would probably have made the <code>Labels</code> component, which contains the selected year's values, into a live region. That way, every time the content changes when the year changes, the content would have been announced. </p>
<p>Also, I could have added that to this version, but I wanted to keep this blog post as simple as possible. Also, if a user navigates linearly, they would first encounter the graph and the data before hitting the buttons. That way, there would be context for buttons and a way to navigate through the years. If they are not navigating linearly, then the situation can be different, depending on the mode they're using. </p>
<h3 id="additional-tab-stops">Additional Tab Stops</h3>
<p>Another consideration is that this implementation creates additional tab stops for a keyboard user. It's only two, but if every time you press a key is painful, those two can be too much. </p>
<p>In this case, a similar implementation I described for screen-reader users would also be better for keyboard users. That would reduce a lot of tab stops from the graph and replace them with just these two buttons. Of course, a button needs to be activated with a keypress, so there's that.</p>
<h2 id="wrapping-up">Wrapping Up</h2>
<p>In this blog post, we've looked into how to add visible controls for navigating the graph. This approach has solved issues for those who might have trouble with path-based gestures or use a switch device. We've also looked into some considerations this implementation has related to screen-reader and keyboard navigation. </p>
<p>This blog post series has provided two approaches to solving issues with pointer input-based graph navigation so far: Adding an overlay and focusable areas on top of the years in the graph and adding control buttons. As every app and use case is different, you might need one or both of them to make your graphs more accessible - or, in some cases, a different type of solution can be the answer. </p>
<h2 id="links-in-blog-post">Links in Blog Post</h2>
<ul>
<li><a href="https://eevis.codes/blog/2023-07-24/more-accessible-graphs-with-jetpack-compose-part-1-adding-content/">More Accessible Graphs with Jetpack Compose Part 1: Adding Content Description</a></li>
<li><a href="https://eevis.codes/blog/2023-07-31/more-accessible-graphs-with-jetpack-compose-part-2-adding-keyboard-interaction/">More Accessible Graphs with Jetpack Compose Part 2: Adding Keyboard Interaction</a></li>
<li><a href="https://eevis.codes/blog/2023-08-16/more-accessible-graphs-with-jetpack-compose-part-3-differentiating-without/">More Accessible Graphs with Jetpack Compose Part 3: Differentiating without Color</a></li>
<li><a href="https://github.com/eevajonnapanula/graph-accessibility-example/commit/0b049a57c89a1fc73f0224c0f1e4130871f63ae9">You can find the final code from this commit.</a></li>
</ul>
</article>
How My App Won the 2nd Place in the WWCode App Deploy Hackathon2023-12-14T00:00:00.000Zhttps://eevis.codes/blog/2023-12-14/how-my-app-won-the-2nd-place-in-the-wwcode-app-deploy-hackathon/
<nav id="breadcrumbs" aria-label="breadcrumbs">
<ol>
<li>
<a href="https://eevis.codes/blog/">Blog</a>
<span aria-hidden="true" class="breadcrumb-separator">/<span>
</span></span></li>
<li>How My App Won the 2nd Place in the WWCode App Deploy Hackathon</li><li>
</li></ol>
</nav>
<article class="blogPostLong">
<h1 id="/2023-12-14/how-my-app-won-the-2nd-place-in-the-wwcode-app-deploy-hackathon">How My App Won the 2nd Place in the WWCode App Deploy Hackathon</h1>
<div class="reading-time-and-date">
<p>
Reading time: about 7 minutes
</p>
<span class="separator"></span>
<p>Published <time datetime="14th Dec, 2023">14th Dec, 2023</time></p>
</div>
<section class="likes-and-boosts">
<div>
<svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="boosts-title">
<title id="boosts-title">Boosts</title>
<path d="M7.19 4.35047C5.15 6.64047 3.75 9.93047 3.62 10.2405L0 8.69047L4.05 4.64047C4.52 4.17047 5.2 3.96047 5.86 4.09047L7.19 4.35047ZM9.17 15.0005C9.17 15.0005 12.91 13.4505 15.06 11.3005C20.46 5.90047 19.56 1.68047 19.27 0.730473C18.32 0.430473 14.1 -0.459527 8.7 4.94047C6.55 7.09047 5 10.8305 5 10.8305L9.17 15.0005ZM15.65 12.8105C13.36 14.8505 10.07 16.2505 9.76 16.3805L11.31 20.0005L15.36 15.9505C15.83 15.4805 16.04 14.8005 15.91 14.1405L15.65 12.8105ZM7 16.0005C7 16.8305 6.66 17.5805 6.12 18.1205C4.94 19.3005 0 20.0005 0 20.0005C0 20.0005 0.7 15.0605 1.88 13.8805C2.2988 13.4595 2.83325 13.1724 3.4155 13.0558C3.99775 12.9392 4.60153 12.9982 5.15017 13.2253C5.69882 13.4525 6.16758 13.8376 6.49694 14.3317C6.8263 14.8258 7.0014 15.4067 7 16.0005ZM11 7.00047C11 5.90047 11.9 5.00047 13 5.00047C14.1 5.00047 15 5.90047 15 7.00047C15 8.10047 14.1 9.00047 13 9.00047C11.9 9.00047 11 8.10047 11 7.00047Z" fill="#00696D"></path>
</svg>
0
</div>
<div>
<svg role="img" width="20" height="19" viewBox="0 0 20 19" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="likes-title">
<title id="likes-title">Likes</title>
<path d="M10 18.35L8.55 17.03C3.4 12.36 0 9.27 0 5.5C0 2.41 2.42 0 5.5 0C7.24 0 8.91 0.81 10 2.08C11.09 0.81 12.76 0 14.5 0C17.58 0 20 2.41 20 5.5C20 9.27 16.6 12.36 11.45 17.03L10 18.35Z"></path>
</svg>
0
</div>
</section>
<img class="cover-photo" src="https://images.ctfassets.net/mpqufjsy02zr/YQxqtqj4Rj83syomLFlyc/e5fafc52b946be245ea6084917e0e65d/bragdoc-wwcode.png" alt="How My App Won the 2nd Place in the WWCode App Deploy Hackathon
" />
<div class="blog-tags">
<a href="https://eevis.codes/tags/career"><span class="blog-tag purple">career</span></a> <a href="https://eevis.codes/tags/hackathon"><span class="blog-tag orange">hackathon</span></a>
</div>
<nav aria-label="How My App Won the 2nd Place in the WWCode App Deploy Hackathon Table of Contents" class="table-of-contents">
<h2>Table of Contents</h2>
<ol>
<li><a href="https://eevis.codes/blog/2023-12-14/how-my-app-won-the-2nd-place-in-the-wwcode-app-deploy-hackathon/#planning-for-the-hackathon"> Planning for the Hackathon</a></li><li><a href="https://eevis.codes/blog/2023-12-14/how-my-app-won-the-2nd-place-in-the-wwcode-app-deploy-hackathon/#building-the-app"> Building the App</a></li><li><a href="https://eevis.codes/blog/2023-12-14/how-my-app-won-the-2nd-place-in-the-wwcode-app-deploy-hackathon/#submission"> Submission</a></li><li><a href="https://eevis.codes/blog/2023-12-14/how-my-app-won-the-2nd-place-in-the-wwcode-app-deploy-hackathon/#winner-announcement"> Winner Announcement</a></li><li><a href="https://eevis.codes/blog/2023-12-14/how-my-app-won-the-2nd-place-in-the-wwcode-app-deploy-hackathon/#final-thoughts-and-link-to-code"> Final Thoughts and Link to Code</a></li><li><a href="https://eevis.codes/blog/2023-12-14/how-my-app-won-the-2nd-place-in-the-wwcode-app-deploy-hackathon/#links-in-the-blog-post"> Links in the Blog Post</a></li>
</ol>
</nav>
<p>A few weeks ago, I participated in my first mobile hackathon; it was so much fun! This hackathon was also the first more traditional (so, just over the weekend) for me and the second in general. The first was the Github Actions hackathon in 2020, organized by Dev.to, and it spanned throughout weeks.</p>
<p>The hackathon I participated in was <a href="https://hopin.com/events/wwcode-app-deploy/registration">App Deploy Hackathon from Women Who Code</a>, and the goal was to build an Android app and use any of the Google APIs available. WWCode organized the hackathon in partnership with Google Play.</p>
<p>I can already share that I won second place in the hackathon! You can read more from <a href="https://www.womenwhocode.com/blog/diversifying-the-tech-ecosystem-winners-and-highlights-from-the-wwcode-app-deploy-hackathon-with-google-play">Women Who Code's Winner announcement blog post</a>. </p>
<p>I'll share my journey from planning to submission in this blog post. You'll also find links to the code and a video demonstrating the app. I haven't yet published the app in Google Play because there was not enough time to jump through all the hoops, but I will probably release it sometime at the beginning of next year.</p>
<h2 id="planning-for-the-hackathon">Planning for the Hackathon</h2>
<p>I registered for the hackathon about two weeks before the event. Those two weeks were hectic, but I had some time to think about the hackathon app idea. I spent quite a bit of time with the Google API Explorer and tried to come up with some ideas.</p>
<p>I came up with two more concrete ideas: a menstruation-tracking app with the features I'd love to have with Google Health Connect, or an app for documenting accomplishments and wins at work and generating a text for a performance review cycle from those items. That second one would use the PaLM API (or so I thought). </p>
<p>When the start of the hackathon came closer, I decided to go with the second idea. I researched and had my building plan (or the general lines) ready for action. </p>
<h3 id="the-story-behind-the-app-idea">The Story Behind the App Idea</h3>
<p>The app idea was something that had been on my mind for a long, long time. Someone shared <a href="https://jvns.ca/blog/brag-documents/">Julia Evans' blog post "Get your work recognized: write a brag document"</a> a long time ago, and I loved the idea. I have often started writing my brag documents, but I have always forgotten about them because they have been in forms I don't use that much, like Google Docs or other similar solutions. </p>
<p>So, I wanted to build an app for that - maybe I'll remember it better if I see the application icon on my home screen. That remains to be seen, but now I have an app I can use. </p>
<h2 id="building-the-app">Building the App</h2>
<p>Finally, the hackathon started. I live in Finland (UTC+2), and the event times were in Eastern Time (UTC-5), so for me, the building started only on Saturday morning. </p>
<p>I'm an Android developer and had a good idea of what architecture components I'm using, so I got the basis of the app built and running relatively quickly. Then, it was time to start implementing the actual things that the app does. Everything went smoothly until I was about to integrate the PaLM API into my app. </p>
<h3 id="need-to-switch-the-api">Need to Switch the API</h3>
<p>As I mentioned, I had already planned to use the PaLM API and Vertex AI in the app. I started integrating it, had everything I needed for it to work, and... I got an error that it's not yet available in Finland. I had forgotten to check its availability - like, it's available in many countries, so Finland must surely be one of them. Well, it was not.</p>
<p>My solution relied on some sort of text generation, and the hackathon rules also said that I'd need to use a Google API as part of the app. I didn't have time to start over, so I had to come up with a solution.</p>
<p>I went for a walk to clear my head and returned to my computer with a plan. I'd use Open AI's Chat Completions API for text generation and Firebase Crashlytics as the Google API. </p>
<p>After trial and error, and some prompting, I integrated the Chat Completions API into my app. I felt so great the first time I got the generated text on my screen - a moment I'll definitely store in my memory and my BragDoc app. </p>
<h3 id="integrating-crashlytics">Integrating Crashlytics</h3>
<p>The other thing I needed to add was Crashlytics. I was surprised at how easy it was - I usually encounter some problems when following instructions, but everything went smoothly this time.</p>
<p>It might have something to do with the fact that I've added Firebase Notifications to an app before, and back then, I had a lot of problems. Nothing seemed to work. That was until I realized that I had added the <code>google-services.json</code> to the wrong folder as I had added it manually. This time, I used the helper in Android Studio, and let me tell you, that went smoothly.</p>
<h3 id="the-mvp-of-the-app">The MVP of the App</h3>
<p>The idea of the application is simple: There is a screen where you can add your wins and accomplishments throughout the year. You can see them listed on that screen and delete them if needed - meaning there is no edit option. </p>
<p>There is also another screen for generating a summary of your accomplishments. It takes all the achievements not part of a summary yet and generates a short-ish summary from them with the help of OpenAI's Chat Completions API. You can see all the generated summaries on the same screen.</p>
<p>Here's a video of the app: </p>
<p><a href="https://www.youtube.com/watch?v=UnFLjTYSkeE">https://www.youtube.com/watch?v=UnFLjTYSkeE</a></p>
<h2 id="submission">Submission</h2>
<p>The deadline for submission was on Monday (or, in my time, early Tuesday morning). As I had to work on Monday, I had to get the submission as ready as possible before I started working. That was a busy morning - I had to quickly create all the assets I'd need, the video for the submission, and decide if I wanted to add a possibility to download the app.</p>
<p>At this phase, all the skills I have with Figma and the VN video editor came in handy - without some prior knowledge, I'm not sure if I would have been able to submit everything in time. I'm still slightly unsatisfied with the video, but it is what it is. </p>
<p>I wanted to get the app to the Play Store, but if you've ever submitted anything there, you know it requires time. There are so many questions to answer and checks to complete. I had to rule that option out, and I started wondering what I could do.</p>
<p>Luckily, I was logged in to the Firebase console and noticed a link, "App distribution." I started reading about it and realized that this is exactly what I need. A few moments later, I had the app available through there and could add a link to it to my hackathon submission.</p>
<p>I got it in in time and was so happy and proud. I had started developing something and had made the MVP out of it. It's not perfect, and I could improve many things, but I finished it in time. </p>
<h2 id="winner-announcement">Winner Announcement</h2>
<p>On the morning of December 7th, I opened my email and found a message from Women Who Code congratulating me on being one of the hackathon's winners. I was so happy and honored!</p>
<p>I also want to congratulate the other winners - Marissa Campa with RecipeApp — A Recipe Finder, and Melike Altin, Arzu Caner, Ezgi Efe, and Yagmur Baran Karakus with Travel Brew!</p>
<h2 id="final-thoughts-and-link-to-code">Final Thoughts and Link to Code</h2>
<p>I'm proud of myself for completing this MVP of the hackathon app and being one of the winners. I also did well with the scoping of the app. It's so easy to start building an app that's too big for the time constraints of the hackathon and get lost in different features. A weekend is a short time (especially when you're making something alone), so it needs to be scoped well if the goal is to get something done.</p>
<p>If you want to see the application code, you can find it in my <a href="https://github.com/eevajonnapanula/BragDoc">BragDoc-repository in Github</a>. I will improve and publish the app in the Play Store later, so stay tuned! I will share about it on my socials, so if you want to get your hands on it, follow me on <a href="https://www.linkedin.com/in/eevajonna/">LinkedIn (Eeva-Jonna Panula)</a> or <a href="https://mas.to/@eevis">Mastodon (@eevis@mas.to)</a>.</p>
<h2 id="links-in-the-blog-post">Links in the Blog Post</h2>
<ul>
<li><a href="https://hopin.com/events/wwcode-app-deploy/registration">App Deploy Hackathon from Women Who Code</a> </li>
<li><a href="https://www.womenwhocode.com/blog/diversifying-the-tech-ecosystem-winners-and-highlights-from-the-wwcode-app-deploy-hackathon-with-google-play">Women Who Code's Winner announcement blog post</a></li>
<li><a href="https://jvns.ca/blog/brag-documents/">Julia Evans' blog post "Get your work recognized: write a brag document"</a></li>
<li><a href="https://github.com/eevajonnapanula/BragDoc">BragDoc-repository in Github</a></li>
<li><a href="https://www.linkedin.com/in/eevajonna/">LinkedIn (Eeva-Jonna Panula)</a> </li>
<li><a href="https://mas.to/@eevis">Mastodon (@eevis@mas.to)</a></li>
</ul>
</article>
Year in Review - 2023 Edition2023-12-29T00:00:00.000Zhttps://eevis.codes/blog/2023-12-29/year-in-review-2023-edition/
<nav id="breadcrumbs" aria-label="breadcrumbs">
<ol>
<li>
<a href="https://eevis.codes/blog/">Blog</a>
<span aria-hidden="true" class="breadcrumb-separator">/<span>
</span></span></li>
<li>Year in Review - 2023 Edition</li><li>
</li></ol>
</nav>
<article class="blogPostLong">
<h1 id="/2023-12-29/year-in-review-2023-edition">Year in Review - 2023 Edition</h1>
<div class="reading-time-and-date">
<p>
Reading time: about 7 minutes
</p>
<span class="separator"></span>
<p>Published <time datetime="29th Dec, 2023">29th Dec, 2023</time></p>
</div>
<section class="likes-and-boosts">
<div>
<svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="boosts-title">
<title id="boosts-title">Boosts</title>
<path d="M7.19 4.35047C5.15 6.64047 3.75 9.93047 3.62 10.2405L0 8.69047L4.05 4.64047C4.52 4.17047 5.2 3.96047 5.86 4.09047L7.19 4.35047ZM9.17 15.0005C9.17 15.0005 12.91 13.4505 15.06 11.3005C20.46 5.90047 19.56 1.68047 19.27 0.730473C18.32 0.430473 14.1 -0.459527 8.7 4.94047C6.55 7.09047 5 10.8305 5 10.8305L9.17 15.0005ZM15.65 12.8105C13.36 14.8505 10.07 16.2505 9.76 16.3805L11.31 20.0005L15.36 15.9505C15.83 15.4805 16.04 14.8005 15.91 14.1405L15.65 12.8105ZM7 16.0005C7 16.8305 6.66 17.5805 6.12 18.1205C4.94 19.3005 0 20.0005 0 20.0005C0 20.0005 0.7 15.0605 1.88 13.8805C2.2988 13.4595 2.83325 13.1724 3.4155 13.0558C3.99775 12.9392 4.60153 12.9982 5.15017 13.2253C5.69882 13.4525 6.16758 13.8376 6.49694 14.3317C6.8263 14.8258 7.0014 15.4067 7 16.0005ZM11 7.00047C11 5.90047 11.9 5.00047 13 5.00047C14.1 5.00047 15 5.90047 15 7.00047C15 8.10047 14.1 9.00047 13 9.00047C11.9 9.00047 11 8.10047 11 7.00047Z" fill="#00696D"></path>
</svg>
0
</div>
<div>
<svg role="img" width="20" height="19" viewBox="0 0 20 19" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="likes-title">
<title id="likes-title">Likes</title>
<path d="M10 18.35L8.55 17.03C3.4 12.36 0 9.27 0 5.5C0 2.41 2.42 0 5.5 0C7.24 0 8.91 0.81 10 2.08C11.09 0.81 12.76 0 14.5 0C17.58 0 20 2.41 20 5.5C20 9.27 16.6 12.36 11.45 17.03L10 18.35Z"></path>
</svg>
0
</div>
</section>
<img class="cover-photo" src="https://images.ctfassets.net/mpqufjsy02zr/35tEZ1i0m9Cb1SRMB6Xdnp/2c9b8fe1b055b71d8276256ddc7b2644/yir-2023-square.png" alt="Year in Review - 2023 Edition" />
<div class="blog-tags">
<a href="https://eevis.codes/tags/yearly-review"><span class="blog-tag turquoise">yearly-review</span></a>
</div>
<nav aria-label="Year in Review - 2023 Edition Table of Contents" class="table-of-contents">
<h2>Table of Contents</h2>
<ol>
<li><a href="https://eevis.codes/blog/2023-12-29/year-in-review-2023-edition/#accomplishments"> Accomplishments</a></li><li><a href="https://eevis.codes/blog/2023-12-29/year-in-review-2023-edition/#speaking"> Speaking</a></li><li><a href="https://eevis.codes/blog/2023-12-29/year-in-review-2023-edition/#writing"> Writing</a></li><li><a href="https://eevis.codes/blog/2023-12-29/year-in-review-2023-edition/#other-things"> Other Things</a></li><li><a href="https://eevis.codes/blog/2023-12-29/year-in-review-2023-edition/#what-about-2024"> What About 2024?</a></li><li><a href="https://eevis.codes/blog/2023-12-29/year-in-review-2023-edition/#links-in-blog-post"> Links in Blog Post</a></li>
</ol>
</nav>
<p>Another year, another Year in Review blog post! When I started listing things from 2023, I realized a lot had happened. And I probably don't even remember everything. </p>
<p>I've written similar posts before, and you can find them from:</p>
<ul>
<li><a href="https://eevis.codes/blog/2022-01-01/year-in-review-2021-edition/">Year in Review - 2021 Edition</a></li>
<li><a href="https://eevis.codes/blog/2023-01-07/year-in-review-2022-edition/">Year in Review - 2022 Edition</a></li>
</ul>
<h2 id="accomplishments">Accomplishments</h2>
<p>There are so many things I've accomplished this year. One of them is surviving through the year. It has been one of the most challenging years so far. But more about that later. </p>
<p>My latest accomplishment was winning 2nd place in the WWCode App Deploy hackathon. I was so proud of the app, and even more proud when I got the letter that they selected my app as second best. If you want to know more about the app, I wrote a blog post: <a href="https://eevis.codes/blog/2023-12-14/how-my-app-won-the-2nd-place-in-the-wwcode-app-deploy-hackathon/">How My App Won the 2nd Place in the WWCode App Deploy Hackathon</a>. </p>
<p>Another thing I want to mention was completing the Women Developer Academy in October. I'm so happy about meeting all the people and getting to know them. I also can't wait for all the future conferences and other events where we'll meet in person! I wrote a blog post about my experiences: <a href="https://eevis.codes/blog/2023-11-24/women-developer-academy-europe-2023-my-experience/">Women Developer Academy Europe 2023 - My Experience</a></p>
<p>During the fall, we organized a study group for learning Android with my colleague at Oura. It was for a Finnish organization called Mimmit Koodaa (Women Code would be the literal translation), and we got a great group of Android newbies joining us throughout the months. Even though fall turned out to be full of events and other things, I'm happy that we got to organize this study group. </p>
<p>Another notable thing is that I was a guest on two podcasts: <a href="https://www.techtalkswithmadona.com/p/season-3-episode-2-what-does-it-mean">Tech Talks with Madona</a> and <a href="https://www.womenwhocode.com/blog/conversations-87-web-accessibility">Women Who Code podcast</a>. It was so fun to record both of them! I recommend checking both podcasts out (not just the episodes I'm in); there are so many gems in the episodes. </p>
<p>Finally, I'm Dev Top Author for the second year in a row! Dev has always been a dear community to me, as it has been so welcoming and inclusive, so this is an honor. </p>
<h2 id="speaking">Speaking</h2>
<p>In 2023, I did more in-person talks than last year. Most of the engagements were conferences, with a couple of meetups.</p>
<ul>
<li>Conferences:<ul>
<li>Android Worldwide</li>
<li>Women Who Code Dev Summit - Mobile & Web</li>
<li>Droidcon Italy</li>
<li>KKON</li>
<li>Droidcon London</li>
<li>Saavuta 2023</li>
</ul>
</li>
<li>Meetups<ul>
<li>Aurajoki Overflow</li>
<li>Accessibility Morning Mixer</li>
</ul>
</li>
</ul>
<p>I also had to cancel two talks: RN EU and DevFest Stockholm. </p>
<p>Even though I enjoyed every talk I gave, the most meaningful were Droidcons. Those were both in person, and I got to travel to Turin and London, both for the very first time. Meeting everyone there made me really feel like I'm part of the Android community. And as they were in-person conferences, that facilitated some very interesting discussions. </p>
<p>And there was one more thing: I've struggled a lot at work, feeling like I'm not seen as the professional I am, so being a speaker and having all those conversations felt great. </p>
<p>Even though I didn't give that many talks last year, I'm happy about the ones I gave. So, it was a good year in terms of speaking.</p>
<h2 id="writing">Writing</h2>
<p>2023 was alright from a writing perspective. I published 16 blog posts and found new mediums, like Medium (pun intended). I mainly wrote about Android-related topics, with some more career or inclusion-related posts. </p>
<p>I started publishing on Medium, and many of my posts were published on ProAndroidDev-publication. One of my posts also got boosted by the Medium team, which was a pleasant surprise. </p>
<p>Many newsletters featured my blog posts, which was also nice. I made it to Dev's week's top 7 list twice, first with my #WeCoded-post "<a href="https://dev.to/eevajonnapanula/the-language-we-use-matters-9mn">The Language We Use Matters</a>" and then with my recent post "How My App Won the 2nd Place in the WWCode App Deploy Hackathon". </p>
<p>Here are some personal favorites:</p>
<article class="blog-embed">
<h3>Improving Android Accessibility with Modifiers in Jetpack Compose
</h3>
<p>Published at <time datetime="2023-07-11">July 11th</time></p>
<a href="https://eevis.codes/blog/2023-07-11/improving-android-accessibility-with-modifiers-in-jetpack-compose/">Read the post Improving Android Accessibility with Modifiers in Jetpack Compose
</a>
</article>
<p>I wrote about using Jetpack Compose's modifiers to improve accessibility. In the blog post, I cover four modifiers: <code>clickable</code>, <code>toggleable</code>, <code>selectable</code>, and <code>magnifier</code>. This post is also something I've turned into a talk; for example, I gave a talk by the same name and contents at Droidcon Italy.</p>
<article class="blog-embed">
<h3>Sometimes I Feel Like I'm Invisible - Experiences of a Woman in Tech</h3>
<p>Published at <time datetime="2023-02-05">February 5th</time></p>
<a href="https://eevis.codes/blog/2023-02-05/sometimes-i-feel-like-im-invisible-experiences-of-a-woman-in-tech/">Read the post Sometimes I Feel Like I'm Invisible - Experiences of a Woman in Tech</a>
</article>
<p>Another personal favorite is this blog post about how I sometimes feel invisible as a woman in tech. In the post, I wrote about personal experiences and microaggressions in general. I wrote it after some events at work, but it wasn't just about those events - it's something I've been feeling my whole career. </p>
<article class="blog-embed">
<h3>More Accessible Graphs with Jetpack Compose Part 4: On-Screen Control Buttons</h3>
<p>Published at <time datetime="2023-12-09">December 9th</time></p>
<a href="https://eevis.codes/blog/2023-12-09/more-accessible-graphs-with-jetpack-compose-part-4-on-screen-control-buttons/">Read the post More Accessible Graphs with Jetpack Compose Part 4: On-Screen Control Buttons</a>
</article>
<p>This blog post was fun to write. Or, rather, to build. In the series of blog posts about more accessible graphs, I've been improving a line graph, and this blog post adds on-screen control buttons for easier navigation. It was also the blog post that got boosted by the Medium team. </p>
<h2 id="other-things">Other Things</h2>
<p>Of course, this year has been about more than just writing, talking, and accomplishments. It's not a secret that this year, once again, has been a fight with burnout for me. I wish I could tell you a story about how I was burnt out and re-discovered myself, and now I'm one of those success stories out there. </p>
<p>No, the story is way more boring and closer to reality for many living with burnout. There have been better times, and there have been worse times. I wish taking sickness leave would solve this, but it hasn't, so here we are, trying to solve the problems at work that cause this. And it doesn't help that because of the brain injury I had, I'm more prone to burnout than some others.</p>
<p>But this year hasn't been all bad either! I cherish many beautiful moments - like when I went kayaking in spring to one of the Finnish national parks (Kolovesi), home to the Saimaa ringed seal. I saw many of them from a distance, but when I unpacked my kayak on the dock at the end of the trip, I heard a sound behind me. I turned around, and I saw that there had been something big. A large fish, I thought to myself and continued unpacking. </p>
<p>Then I realized that someone was staring at me from the other side of the dock. It was Saimaa ringed seal! I slowly tried to get my phone to get a picture, and right before I was ready to take the photo, the seal just looked at me with those side-eyes (you know, the same that dogs do often) and slowly disappeared to the water. So, no, I didn't get a photo. But the memory will live forever.</p>
<p>Another notable thing from 2023 was that I started writing my master's thesis. It's about Android developers and how to help us to make more accessible apps. I plan to finish it by summer. I'm taking part-time study leave for that, but we'll see what the spring brings. I've learned to not to plan too strictly. </p>
<p>I've tried to develop new hobbies to improve my recovery outside work, and I started golf. First, going to the green card course was just to be able to join my sister when she's playing, but I kind of fell into a rabbit hole, and now I'm taking lessons during winter to improve my game, and I can't wait for the snow to melt to get to play.</p>
<h2 id="what-about-2024">What About 2024?</h2>
<p>I'm not making many promises for 2024 either (as I didn't do last year), but I hope to finish my master's thesis and graduate next year. I also hope to find balance and write a different type of year in review a year from now. </p>
<p>There are also some longer-term dreams, like writing a book. I've been dreaming about it for a long time, and the time to make that dream come true is closer than it used to be. But first, I really need to finish that master's thesis. </p>
<p>Overall, I hope 2024 will bring peace, time with old and new friends, a lot of time spent outside, and lots of new learnings. </p>
<p>I wish you a better year in 2024!</p>
<h2 id="links-in-blog-post">Links in Blog Post</h2>
<ul>
<li><a href="https://eevis.codes/blog/2022-01-01/year-in-review-2021-edition/">Year in Review - 2021 Edition</a></li>
<li><a href="https://eevis.codes/blog/2023-01-07/year-in-review-2022-edition/">Year in Review - 2022 Edition</a></li>
<li><a href="https://eevis.codes/blog/2023-12-14/how-my-app-won-the-2nd-place-in-the-wwcode-app-deploy-hackathon/">How My App Won the 2nd Place in the WWCode App Deploy Hackathon</a></li>
<li><a href="https://eevis.codes/blog/2023-11-24/women-developer-academy-europe-2023-my-experience/">Women Developer Academy Europe 2023 - My Experience</a></li>
<li><a href="https://www.techtalkswithmadona.com/p/season-3-episode-2-what-does-it-mean">Tech Talks with Madona</a> </li>
<li><a href="https://www.womenwhocode.com/blog/conversations-87-web-accessibility">Women Who Code podcast</a></li>
<li><a href="https://dev.to/eevajonnapanula/the-language-we-use-matters-9mn">The Language We Use Matters</a></li>
<li><a href="https://eevis.codes/blog/2023-07-11/improving-android-accessibility-with-modifiers-in-jetpack-compose/">Read the post Improving Android Accessibility with Modifiers in Jetpack Compose</a></li>
<li><a href="https://eevis.codes/blog/2023-02-05/sometimes-i-feel-like-im-invisible-experiences-of-a-woman-in-tech/">Read the post Sometimes I Feel Like I'm Invisible - Experiences of a Woman in Tech</a></li>
<li><a href="https://eevis.codes/blog/2023-12-09/more-accessible-graphs-with-jetpack-compose-part-4-on-screen-control-buttons/">Read the post More Accessible Graphs with Jetpack Compose Part 4: On-Screen Control Buttons</a></li>
</ul>
</article>
Exploring Health Connect Pt. 1 - Setting Up Permissions2024-01-12T00:00:00.000Zhttps://eevis.codes/blog/2024-01-12/exploring-health-connect-pt-1-setting-up-permissions/
<nav id="breadcrumbs" aria-label="breadcrumbs">
<ol>
<li>
<a href="https://eevis.codes/blog/">Blog</a>
<span aria-hidden="true" class="breadcrumb-separator">/<span>
</span></span></li>
<li>Exploring Health Connect Pt. 1 - Setting Up Permissions</li><li>
</li></ol>
</nav>
<article class="blogPostLong">
<h1 id="/2024-01-12/exploring-health-connect-pt-1-setting-up-permissions">Exploring Health Connect Pt. 1 - Setting Up Permissions</h1>
<div class="reading-time-and-date">
<p>
Reading time: about 7 minutes
</p>
<span class="separator"></span>
<p>Published <time datetime="12th Jan, 2024">12th Jan, 2024</time></p>
</div>
<section class="likes-and-boosts">
<div>
<svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="boosts-title">
<title id="boosts-title">Boosts</title>
<path d="M7.19 4.35047C5.15 6.64047 3.75 9.93047 3.62 10.2405L0 8.69047L4.05 4.64047C4.52 4.17047 5.2 3.96047 5.86 4.09047L7.19 4.35047ZM9.17 15.0005C9.17 15.0005 12.91 13.4505 15.06 11.3005C20.46 5.90047 19.56 1.68047 19.27 0.730473C18.32 0.430473 14.1 -0.459527 8.7 4.94047C6.55 7.09047 5 10.8305 5 10.8305L9.17 15.0005ZM15.65 12.8105C13.36 14.8505 10.07 16.2505 9.76 16.3805L11.31 20.0005L15.36 15.9505C15.83 15.4805 16.04 14.8005 15.91 14.1405L15.65 12.8105ZM7 16.0005C7 16.8305 6.66 17.5805 6.12 18.1205C4.94 19.3005 0 20.0005 0 20.0005C0 20.0005 0.7 15.0605 1.88 13.8805C2.2988 13.4595 2.83325 13.1724 3.4155 13.0558C3.99775 12.9392 4.60153 12.9982 5.15017 13.2253C5.69882 13.4525 6.16758 13.8376 6.49694 14.3317C6.8263 14.8258 7.0014 15.4067 7 16.0005ZM11 7.00047C11 5.90047 11.9 5.00047 13 5.00047C14.1 5.00047 15 5.90047 15 7.00047C15 8.10047 14.1 9.00047 13 9.00047C11.9 9.00047 11 8.10047 11 7.00047Z" fill="#00696D"></path>
</svg>
0
</div>
<div>
<svg role="img" width="20" height="19" viewBox="0 0 20 19" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="likes-title">
<title id="likes-title">Likes</title>
<path d="M10 18.35L8.55 17.03C3.4 12.36 0 9.27 0 5.5C0 2.41 2.42 0 5.5 0C7.24 0 8.91 0.81 10 2.08C11.09 0.81 12.76 0 14.5 0C17.58 0 20 2.41 20 5.5C20 9.27 16.6 12.36 11.45 17.03L10 18.35Z"></path>
</svg>
0
</div>
</section>
<img class="cover-photo" src="https://images.ctfassets.net/mpqufjsy02zr/2mfp2KPJcCxHHi0WoYLQN9/4a6282d98ac92a1d822d4f0f96875f72/health-connect-permissions.png" alt="Exploring Health Connect - Setting Up Permissions " />
<div class="blog-tags">
<a href="https://eevis.codes/tags/Android"><span class="blog-tag blue">Android</span></a> <a href="https://eevis.codes/tags/Kotlin"><span class="blog-tag turquoise">Kotlin</span></a> <a href="https://eevis.codes/tags/Health%20Connect"><span class="blog-tag turquoise">Health Connect</span></a>
</div>
<nav aria-label="Exploring Health Connect Pt. 1 - Setting Up Permissions Table of Contents" class="table-of-contents">
<h2>Table of Contents</h2>
<ol>
<li><a href="https://eevis.codes/blog/2024-01-12/exploring-health-connect-pt-1-setting-up-permissions/#what-is-health-connect"> What is Health Connect?</a></li><li><a href="https://eevis.codes/blog/2024-01-12/exploring-health-connect-pt-1-setting-up-permissions/#starting-point-for-the-app"> Starting Point for the App</a></li><li><a href="https://eevis.codes/blog/2024-01-12/exploring-health-connect-pt-1-setting-up-permissions/#setup"> Setup</a></li><li><a href="https://eevis.codes/blog/2024-01-12/exploring-health-connect-pt-1-setting-up-permissions/#what-if-user-doesnt-give-permission"> What If User Doesn't Give Permission?</a></li><li><a href="https://eevis.codes/blog/2024-01-12/exploring-health-connect-pt-1-setting-up-permissions/#wrapping-up"> Wrapping Up</a></li><li><a href="https://eevis.codes/blog/2024-01-12/exploring-health-connect-pt-1-setting-up-permissions/#links-in-the-blog-post"> Links in the Blog Post</a></li>
</ol>
</nav>
<p>I've been really intrigued to try out Google Health Connect and build an app that utilizes it. When I started thinking about what I'd make, I realized that the one thing that I would need is a custom period tracker. </p>
<p>There are some apps to track your menstruation, but they're usually pretty complex, and most require an account. And when all I want to do is track the times when I'm bleeding, I thought I could build my own app.</p>
<p>Also, many of these apps are often so freaking heteronormative that I don't want to use them. What if I don't have just (CIS) men as potential partners? What if I don't need to care about possible pregnancy? What if I only want to track when I'm bleeding? </p>
<p>Also, why are these apps often colored pink? And why do they (almost) always assume it's women using these apps? Some non-binary folks and trans-men menstruate, too, you know. And yes, some apps are more inclusive than others. However, it is still frustrating how many apps make these assumptions about people who menstruate. </p>
<p>So yes. I decided to build my own app. I'm also writing a couple of blog posts that will share the steps I took to make the first version of my period-tracking app. Even though I'm writing about the menstruation-related Google Health records, these same steps apply to other records. </p>
<p>In this first blog post, I'm sharing how to set things up and ask for permission from the user. The following blog post (published next week) will be about writing and reading data from Health Connect. After these two posts, the app I'm building will look like this:</p>
<video controls="" class="portrait-video">
<source src="https://videos.ctfassets.net/mpqufjsy02zr/3bI9reg5JMR37elhZZfKL3/e45f9e3ac9c0e148d54e461d6caeef14/period_app_rec.mp4" type="video/mp4" />
</video>
<p>Before diving into the coding, let's discuss Health Connect a bit.</p>
<h2 id="what-is-health-connect">What is Health Connect?</h2>
<p>Health Connect is a way to share data between apps on Android. It provides a centralized way to handle health and fitness data, giving users control over which apps they want to share their data. You can learn more from <a href="https://health.google/health-connect-android/">Health Connect by Android site</a>.</p>
<p>As Health Connect is related to sensitive data, you must fill out a form to use it in production. I'm not pushing this app to production anytime soon, so I have yet to check that procedure out. You can read more from the <a href="https://developer.android.com/health-and-fitness/guides/health-connect/publish/request-access">"Request access to Health Connect data types"-documentation</a>.</p>
<h2 id="starting-point-for-the-app">Starting Point for the App</h2>
<p>So before I started implementing integration to Health Connect, I built the UI ready. The app is simple: It has one screen that lists all the periods and a dialog to pick the start and end dates. Here's a picture of the UI:</p>
<img src="https://images.ctfassets.net/mpqufjsy02zr/2s96dAvyRhu0DnyiYAYlN1/88bea8cb594b6820a1cff5ed2f428e06/period-app-start.png" alt="PeriodApp Ui, which has menstruation periods listed. The data is grouped into years, and under each year each period is displayed visually as thick red line over a wider, white rectangle. The start and end dates are marked above the start and end of the red line. The topmost period does not have end date, and the color fades to white." class="portrait-img" />
<p>The code from where we're starting to build this is in <a href="https://github.com/eevajonnapanula/PeriodApp/tree/start">the start-branch</a>. </p>
<h2 id="setup">Setup</h2>
<p>To be able to use Health Connect, we need to do a couple of things: Add necessary dependencies, add required permissions to <code>AndroidManifest</code>, and ask permission to use Health Connect data from the user. Let's start with the first two. </p>
<h3 id="add-dependencies-and-permissions-to-androidmanifest">Add Dependencies and Permissions to AndroidManifest</h3>
<p>A separate Health Connect client is available, and we need to add it to module-level <code>build.gradle.kts</code>. By the time of writing this blog post, the latest version is <code>1.1.0-alpha06</code>, but be sure to <a href="https://developer.android.com/jetpack/androidx/releases/health-connect">check the latest release from Jetpack library versions.</a></p>
<p>So, let's add this line to module-level <code>build.gradle.kts</code>: </p>
<pre><code class="language-kotlin"><span class="token comment">// build.gradle.kts</span>
<span class="token function">implementation</span><span class="token punctuation">(</span><span class="token string-literal singleline"><span class="token string">"androidx.health.connect:connect-client:1.1.0-alpha06"</span></span><span class="token punctuation">)</span>
</code></pre><p>We also need to add the permissions to <code>AndroidManifest</code>. The following two lines are enough as this application only reads and writes menstruation-related data. They go just before the <code><application></code>-element. </p>
<pre><code class="language-xml">// AndroidManifest.xml
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>uses-permission</span>
<span class="token attr-name"><span class="token namespace">android:</span>name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>android.permission.health.READ_MENSTRUATION<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>uses-permission</span>
<span class="token attr-name"><span class="token namespace">android:</span>name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>android.permission.health.WRITE_MENSTRUATION<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span>
</code></pre><p>You can read more about the permissions for each record Health Connect provides from <a href="https://developer.android.com/health-and-fitness/guides/health-connect/plan/data-types#permissions">the documentation's "Permissions"-section</a>.</p>
<h3 id="ask-for-health-connect-permissions">Ask for Health Connect Permissions</h3>
<p>The next thing to do is ask the user for permission. In the context of this app, we want to read the data when the user opens the app, so we need to ask for permission when the user opens the app for the first time (or any time if the permissions are not granted). </p>
<p>We could implement a second check for writing permissions when the user wants to save data to Health Connect. However, I'm trying to make things straightforward, so in this app, we'll ask for both writing and reading permissions simultaneously, and the user needs to give them both to be able to use the app. </p>
<p>So, let's start by adding an <code>intent-filter</code> to <code>MainActivity</code> in <code>AndroidManifest</code> so that we can actually open the permissions dialog:</p>
<pre><code class="language-xml">// AndroidManifest.xml
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>intent-filter</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>action</span>
<span class="token attr-name"><span class="token namespace">android:</span>name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>android.intent.action.VIEW_PERMISSION_USAGE<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>category</span>
<span class="token attr-name"><span class="token namespace">android:</span>name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>android.intent.category.HEALTH_PERMISSIONS<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>intent-filter</span><span class="token punctuation">></span></span>
</code></pre><p>Without these two, the permission dialog won't open. In a production app, you'd probably add a privacy policy screen, and these intent filters would be part of that activity. Read more in <a href="https://developer.android.com/health-and-fitness/guides/health-connect/develop/get-started#show-privacy-policy">Show privacy policy section in Health Connect documentation</a>.</p>
<p>Next, let's create a new file and name it <code>HealthConnectManager.</code> It will serve as a connection point to Health Connect. Inside the file, we declare a class and pass it context as a parameter:</p>
<pre><code class="language-kotlin"><span class="token comment">// HealthConnectManager.kt</span>
<span class="token keyword">class</span> <span class="token function">HealthConnectManager</span><span class="token punctuation">(</span><span class="token keyword">private</span> <span class="token keyword">val</span> context<span class="token operator">:</span> Context<span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token operator">..</span><span class="token punctuation">.</span>
<span class="token punctuation">}</span>
</code></pre><p>To be able to ask for the permissions, we need three things inside the class:</p>
<pre><code class="language-kotlin"><span class="token comment">// HealthConnectManager.kt</span>
<span class="token keyword">private</span> <span class="token keyword">val</span> healthConnectClient <span class="token keyword">by</span> lazy <span class="token punctuation">{</span>
HealthConnectClient<span class="token punctuation">.</span><span class="token function">getOrCreate</span><span class="token punctuation">(</span>context<span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token keyword">suspend</span> <span class="token keyword">fun</span> <span class="token function">hasAllPermissions</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">:</span> Boolean <span class="token operator">=</span>
healthConnectClient
<span class="token punctuation">.</span>permissionController
<span class="token punctuation">.</span><span class="token function">getGrantedPermissions</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">containsAll</span><span class="token punctuation">(</span>PERMISSIONS<span class="token punctuation">)</span>
<span class="token keyword">fun</span> <span class="token function">requestPermissionsActivityContract</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">:</span> ActivityResultContract<span class="token operator"><</span>Set<span class="token operator"><</span>String<span class="token operator">></span><span class="token punctuation">,</span> Set<span class="token operator"><</span>String<span class="token operator">></span><span class="token operator">></span> <span class="token punctuation">{</span>
<span class="token keyword">return</span> PermissionController
<span class="token punctuation">.</span><span class="token function">createRequestPermissionResultContract</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
</code></pre><p>We store the <code>healthConnectClient</code> we use later as a private variable and lazily initialize it. We're also implementing a check for permissions we need and getting the activity result contract.</p>
<p>In addition, we define the permissions we need as a separate variable. In the case of this app, and at this point, we need just two:</p>
<pre><code class="language-kotlin"><span class="token comment">// HealthConnectManager.kt</span>
<span class="token keyword">val</span> PERMISSIONS <span class="token operator">=</span>
<span class="token function">setOf</span><span class="token punctuation">(</span>
HealthPermission
<span class="token punctuation">.</span><span class="token function">getReadPermission</span><span class="token punctuation">(</span>MenstruationPeriodRecord<span class="token operator">::</span><span class="token keyword">class</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
HealthPermission
<span class="token punctuation">.</span><span class="token function">getWritePermission</span><span class="token punctuation">(</span>MenstruationPeriodRecord<span class="token operator">::</span><span class="token keyword">class</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
<span class="token punctuation">)</span>
</code></pre><p>The view model is another layer we'll need to define between the API and UI. The view model needs an instance of <code>HealthConnectManager</code>, which we pass in as a parameter. </p>
<p>We also define a variable to store whether the app has all permissions, a variable to store the permission launcher, and a function to check the permissions.</p>
<pre><code class="language-kotlin"><span class="token comment">// PeriodViewModel.kt</span>
<span class="token keyword">var</span> permissionsGranted <span class="token keyword">by</span> <span class="token function">mutableStateOf</span><span class="token punctuation">(</span><span class="token boolean">false</span><span class="token punctuation">)</span>
<span class="token keyword">val</span> permissionsLauncher <span class="token operator">=</span>
healthConnectManager
<span class="token punctuation">.</span><span class="token function">requestPermissionsActivityContract</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token keyword">private</span> <span class="token keyword">fun</span> <span class="token function">checkPermissions</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
viewModelScope<span class="token punctuation">.</span><span class="token function">launch</span> <span class="token punctuation">{</span>
permissionsGranted <span class="token operator">=</span>
healthConnectManager
<span class="token punctuation">.</span><span class="token function">hasAllPermissions</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>Now, we have all we need to implement the permission check. In <code>MainScreen</code>-composable, we'll define the actual launcher with <code>rememberLauncherForActivityResult</code>, and pass in the <code>permissionsLauncher</code> we defined in the view model:</p>
<pre><code class="language-kotlin"><span class="token comment">// MainScreen.kt</span>
<span class="token keyword">val</span> permissionsLauncher <span class="token operator">=</span>
<span class="token function">rememberLauncherForActivityResult</span><span class="token punctuation">(</span>
viewModel<span class="token punctuation">.</span>permissionsLauncher
<span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token comment">// TODO</span>
<span class="token punctuation">}</span>
</code></pre><p>We will fetch the records in the next blog post in the TODO-block.</p>
<p>After that, we use <code>LaunchedEffect</code> to check if the user has given permissions:</p>
<pre><code class="language-kotlin"><span class="token comment">// MainScreen.kt</span>
<span class="token function">LaunchedEffect</span><span class="token punctuation">(</span>Unit<span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>viewModel<span class="token punctuation">.</span>permissionsGranted<span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token comment">// TODO</span>
<span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
permissionsLauncher<span class="token punctuation">.</span><span class="token function">launch</span><span class="token punctuation">(</span>PERMISSIONS<span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>Here again, the TODO-block is where we'll call the function to get records. </p>
<p>With these changes, the app will prompt the user to give the necessary Health Connect permissions, as seen in the video at the beginning of this blog post. </p>
<h2 id="what-if-user-doesnt-give-permission">What If User Doesn't Give Permission?</h2>
<p>There is just one more thing in the context of this blog post. What if the user doesn't give Health Connect permissions to the app? Or what if they accidentally press cancel when the permission screen is prompted?</p>
<p>We'll need to add a possibility to re-ask the permissions. In the <code>LazyColumn</code> in <code>MainScreen</code>, let's add an <code>item</code> with a <code>Button</code> conditionally as the first thing in the column if the user has not granted permissions:</p>
<pre><code class="language-kotlin"><span class="token comment">// MainScreen.kt </span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>viewModel<span class="token punctuation">.</span>permissionsGranted<span class="token punctuation">.</span><span class="token function">not</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
item <span class="token punctuation">{</span>
<span class="token function">Button</span><span class="token punctuation">(</span>onClick <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token operator">..</span><span class="token punctuation">.</span><span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token function">Text</span><span class="token punctuation">(</span><span class="token string-literal singleline"><span class="token string">"Give permissions to Health Connect"</span></span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>This button would be visible only when the permissions are not granted. </p>
<p>When the user presses the button, the settings for Health Connect should open. The following code does that:</p>
<pre><code class="language-kotlin"><span class="token comment">// MainScreen.kt</span>
<span class="token function">Button</span><span class="token punctuation">(</span>
onClick <span class="token operator">=</span> <span class="token punctuation">{</span>
<span class="token keyword">val</span> intent <span class="token operator">=</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>Build<span class="token punctuation">.</span>VERSION<span class="token punctuation">.</span>SDK_INT <span class="token operator">>=</span> Build<span class="token punctuation">.</span>VERSION_CODES<span class="token punctuation">.</span>UPSIDE_DOWN_CAKE<span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token comment">// HCM is an import alias for HealthConnectManager from the Health Connect client</span>
<span class="token function">Intent</span><span class="token punctuation">(</span>HCM<span class="token punctuation">.</span>ACTION_MANAGE_HEALTH_PERMISSIONS<span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">putExtra</span><span class="token punctuation">(</span>
Intent<span class="token punctuation">.</span>EXTRA_PACKAGE_NAME<span class="token punctuation">,</span>
context<span class="token punctuation">.</span>packageName
<span class="token punctuation">)</span>
<span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
<span class="token function">Intent</span><span class="token punctuation">(</span>
HealthConnectClient<span class="token punctuation">.</span>ACTION_HEALTH_CONNECT_SETTINGS
<span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token function">startActivity</span><span class="token punctuation">(</span>context<span class="token punctuation">,</span> intent<span class="token punctuation">,</span> <span class="token keyword">null</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token operator">..</span><span class="token punctuation">.</span> <span class="token punctuation">}</span>
</code></pre><p>In the <code>onClick</code>-handler, we first check if the user has Android 14 (SDK-version 34), as Health Connect permission management is slightly different starting from that version. If the answer is yes, we set the intent to be <code>HealthConnectManager.ACTION_MANAGE_HEALTH_PERMISSIONS</code> with the extra of the package name to open the dedicated Health Connect permission management screen. </p>
<p>If the user has an earlier version, then the intent is <code>HealthConnectClient.ACTION_HEALTH_CONNECT_SETTINGS</code>, the general Health Connect settings page.</p>
<p>You can find all the changes done in this blog post in this commit: <a href="https://github.com/eevajonnapanula/PeriodApp/commit/b85b08fea2c694e44312776972b6cc830e84af31">Add Health Connect Permissions</a></p>
<h2 id="wrapping-up">Wrapping Up</h2>
<p>In this blog post, we've been looking at how to set up things for Health Connect and how to ask for the permissions from the user. As I'm building this app for my personal use, there might be some corners I've cut, so be sure to check the official documentation if you're building an app that uses Health Connect in production. </p>
<p>The next blog post will be about reading and writing data, as well as updating and deleting it. So stay tuned; I will publish it next week!</p>
<h2 id="links-in-the-blog-post">Links in the Blog Post</h2>
<ul>
<li><a href="https://health.google/health-connect-android/">Health Connect by Android site</a></li>
<li><a href="https://developer.android.com/health-and-fitness/guides/health-connect/publish/request-access">"Request access to Health Connect data types"-documentation</a></li>
<li><a href="https://github.com/eevajonnapanula/PeriodApp/tree/start">the start-branch</a></li>
<li><a href="https://developer.android.com/jetpack/androidx/releases/health-connect">check the latest release from Jetpack library versions.</a></li>
<li><a href="https://developer.android.com/health-and-fitness/guides/health-connect/develop/get-started#show-privacy-policy">Show privacy policy section in Health Connect documentation</a></li>
<li><a href="https://developer.android.com/health-and-fitness/guides/health-connect/plan/data-types#permissions">the documentation's "Permissions"-section</a></li>
<li><a href="https://github.com/eevajonnapanula/PeriodApp/commit/b85b08fea2c694e44312776972b6cc830e84af31">Add Health Connect Permissions</a></li>
</ul>
</article>
Exploring Health Connect Pt. 2 - Reading and Writing Data2024-01-19T00:00:00.000Zhttps://eevis.codes/blog/2024-01-19/exploring-health-connect-pt-2-reading-and-writing-data/
<nav id="breadcrumbs" aria-label="breadcrumbs">
<ol>
<li>
<a href="https://eevis.codes/blog/">Blog</a>
<span aria-hidden="true" class="breadcrumb-separator">/<span>
</span></span></li>
<li>Exploring Health Connect Pt. 2 - Reading and Writing Data</li><li>
</li></ol>
</nav>
<article class="blogPostLong">
<h1 id="/2024-01-19/exploring-health-connect-pt-2-reading-and-writing-data">Exploring Health Connect Pt. 2 - Reading and Writing Data</h1>
<div class="reading-time-and-date">
<p>
Reading time: about 8 minutes
</p>
<span class="separator"></span>
<p>Published <time datetime="19th Jan, 2024">19th Jan, 2024</time></p>
</div>
<section class="likes-and-boosts">
<div>
<svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="boosts-title">
<title id="boosts-title">Boosts</title>
<path d="M7.19 4.35047C5.15 6.64047 3.75 9.93047 3.62 10.2405L0 8.69047L4.05 4.64047C4.52 4.17047 5.2 3.96047 5.86 4.09047L7.19 4.35047ZM9.17 15.0005C9.17 15.0005 12.91 13.4505 15.06 11.3005C20.46 5.90047 19.56 1.68047 19.27 0.730473C18.32 0.430473 14.1 -0.459527 8.7 4.94047C6.55 7.09047 5 10.8305 5 10.8305L9.17 15.0005ZM15.65 12.8105C13.36 14.8505 10.07 16.2505 9.76 16.3805L11.31 20.0005L15.36 15.9505C15.83 15.4805 16.04 14.8005 15.91 14.1405L15.65 12.8105ZM7 16.0005C7 16.8305 6.66 17.5805 6.12 18.1205C4.94 19.3005 0 20.0005 0 20.0005C0 20.0005 0.7 15.0605 1.88 13.8805C2.2988 13.4595 2.83325 13.1724 3.4155 13.0558C3.99775 12.9392 4.60153 12.9982 5.15017 13.2253C5.69882 13.4525 6.16758 13.8376 6.49694 14.3317C6.8263 14.8258 7.0014 15.4067 7 16.0005ZM11 7.00047C11 5.90047 11.9 5.00047 13 5.00047C14.1 5.00047 15 5.90047 15 7.00047C15 8.10047 14.1 9.00047 13 9.00047C11.9 9.00047 11 8.10047 11 7.00047Z" fill="#00696D"></path>
</svg>
0
</div>
<div>
<svg role="img" width="20" height="19" viewBox="0 0 20 19" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="likes-title">
<title id="likes-title">Likes</title>
<path d="M10 18.35L8.55 17.03C3.4 12.36 0 9.27 0 5.5C0 2.41 2.42 0 5.5 0C7.24 0 8.91 0.81 10 2.08C11.09 0.81 12.76 0 14.5 0C17.58 0 20 2.41 20 5.5C20 9.27 16.6 12.36 11.45 17.03L10 18.35Z"></path>
</svg>
0
</div>
</section>
<img class="cover-photo" src="https://images.ctfassets.net/mpqufjsy02zr/53PKKwam2oye19EbNf1Ycr/fead72ee24ce872708b45b2646e6ce01/health-connect-permissions__1_.png" alt="Exploring Health Connect - Reading and Writing Data" />
<div class="blog-tags">
<a href="https://eevis.codes/tags/Kotlin"><span class="blog-tag turquoise">Kotlin</span></a> <a href="https://eevis.codes/tags/Health%20Connect"><span class="blog-tag turquoise">Health Connect</span></a> <a href="https://eevis.codes/tags/Android"><span class="blog-tag blue">Android</span></a>
</div>
<nav aria-label="Exploring Health Connect Pt. 2 - Reading and Writing Data Table of Contents" class="table-of-contents">
<h2>Table of Contents</h2>
<ol>
<li><a href="https://eevis.codes/blog/2024-01-19/exploring-health-connect-pt-2-reading-and-writing-data/#reading-and-writing-data"> Reading and Writing Data</a></li><li><a href="https://eevis.codes/blog/2024-01-19/exploring-health-connect-pt-2-reading-and-writing-data/#wrapping-up"> Wrapping Up</a></li><li><a href="https://eevis.codes/blog/2024-01-19/exploring-health-connect-pt-2-reading-and-writing-data/#links-in-blog-post"> Links in Blog Post</a></li>
</ol>
</nav>
<p>I've been building a period tracking app for myself with the help of Health Connect. Last week, I published the first blog post where I explained how to set up Health Connect and asked the user to give permission to use their data. </p>
<p>I initially thought I'd write one blog post about building this app - now, it's turning into (at least) three. But I have to say that I've enjoyed this process a lot. </p>
<p>This blog post will look into reading and writing data with Health Connect. There will be one more blog post about updating and deleting data before we get to the point where the app looks like in this video: </p>
<video controls="" class="portrait-video">
<source src="https://videos.ctfassets.net/mpqufjsy02zr/3bI9reg5JMR37elhZZfKL3/e45f9e3ac9c0e148d54e461d6caeef14/period_app_rec.mp4" type="video/mp4" />
</video>
<h2 id="reading-and-writing-data">Reading and Writing Data</h2>
<p>Before going into code, I want to mention one handy app: <a href="https://developer.android.com/health-and-fitness/guides/health-connect/test/health-connect-toolbox">Health Connect Toolbox</a>. With its help, you can write and read data from Health Connect, which helps debug issues like if the data was saved at all. Trust me, I know. </p>
<p>Health Connect Toolbox also helps with the first task we're looking into: Reading records. If you don't have any other app writing that particular type of data to Health Connect, you can use the Health Connect Toolbox to write data and then see that data in your own app.</p>
<p>Now, we have the permissions to read and write data from/to Health Connect, so we can add the implementation for those actions.</p>
<h3 id="read-records">Read Records</h3>
<p>The first action we're implementing is reading data. Let's start from the closest point to Health Connect: <code>HealthConnectManager</code>, which we defined in the previous blog post. We need a function to read data from Health Connect:</p>
<pre><code class="language-kotlin"><span class="token comment">// HealthConnectManager.kt </span>
<span class="token keyword">suspend</span> <span class="token keyword">fun</span> <span class="token function">readMenstruationRecords</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">:</span>
List<span class="token operator"><</span>MenstruationPeriodRecord<span class="token operator">></span> <span class="token punctuation">{</span>
<span class="token keyword">val</span> request <span class="token operator">=</span> <span class="token function">ReadRecordsRequest</span><span class="token punctuation">(</span>
recordType <span class="token operator">=</span> MenstruationPeriodRecord<span class="token operator">::</span><span class="token keyword">class</span><span class="token punctuation">,</span>
timeRangeFilter <span class="token operator">=</span>
TimeRangeFilter<span class="token punctuation">.</span><span class="token function">after</span><span class="token punctuation">(</span>
LocalDateTime<span class="token punctuation">.</span><span class="token function">now</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">minusMonths</span><span class="token punctuation">(</span><span class="token number">12</span><span class="token punctuation">)</span>
<span class="token punctuation">)</span><span class="token punctuation">,</span>
<span class="token punctuation">)</span>
<span class="token keyword">val</span> response <span class="token operator">=</span> healthConnectClient<span class="token punctuation">.</span><span class="token function">readRecords</span><span class="token punctuation">(</span>request<span class="token punctuation">)</span>
<span class="token keyword">return</span> response<span class="token punctuation">.</span>records
<span class="token punctuation">}</span>
</code></pre><p>First, we define the request, which is <code>ReadRecordsRequest</code>. The type is <code>MenstruationPeriodRecord::class</code>, as we want to read menstruation period data from Health Connect. We also add a time range filter going back 12 months from today. This end date will probably be changed while developing this application, but for now, let's stick with data from the past 12 months. </p>
<p>Next, we call <code>readRecords</code> from the Health Connect Client. It returns a response with records as one of its values, and we want to return those from this function. </p>
<p>In the view model, we need to make a few more adjustments. Basically, we want to add a wrapper that checks if the app has all permissions to try to read (or write) data. We also need to add the actual data reading function, as well as store the read data to a variable in the view model. </p>
<p>Let's start with the wrapper and add the function: </p>
<pre><code class="language-kotlin"><span class="token comment">// PeriodViewModel.kt</span>
<span class="token keyword">private</span> <span class="token keyword">suspend</span> <span class="token keyword">fun</span> <span class="token function">tryWithPermissionsCheck</span><span class="token punctuation">(</span>
block<span class="token operator">:</span> <span class="token keyword">suspend</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">-></span> Unit
<span class="token punctuation">)</span> <span class="token punctuation">{</span>
permissionsGranted <span class="token operator">=</span>
healthConnectManager<span class="token punctuation">.</span><span class="token function">hasAllPermissions</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token keyword">try</span> <span class="token punctuation">{</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>permissionsGranted<span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token function">block</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span>remoteException<span class="token operator">:</span> RemoteException<span class="token punctuation">)</span> <span class="token punctuation">{</span>
Log<span class="token punctuation">.</span><span class="token function">e</span><span class="token punctuation">(</span><span class="token string-literal singleline"><span class="token string">"Error getting records:"</span></span><span class="token punctuation">,</span> <span class="token string-literal singleline"><span class="token string">"</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span><span class="token expression">remoteException<span class="token punctuation">.</span>message</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">"</span></span><span class="token punctuation">)</span>
<span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span>securityException<span class="token operator">:</span> SecurityException<span class="token punctuation">)</span> <span class="token punctuation">{</span>
Log<span class="token punctuation">.</span><span class="token function">e</span><span class="token punctuation">(</span><span class="token string-literal singleline"><span class="token string">"Error getting records:"</span></span><span class="token punctuation">,</span> <span class="token string-literal singleline"><span class="token string">"</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span><span class="token expression">securityException<span class="token punctuation">.</span>message</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">"</span></span><span class="token punctuation">)</span>
<span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span>ioException<span class="token operator">:</span> IOException<span class="token punctuation">)</span> <span class="token punctuation">{</span>
Log<span class="token punctuation">.</span><span class="token function">e</span><span class="token punctuation">(</span><span class="token string-literal singleline"><span class="token string">"Error getting records:"</span></span><span class="token punctuation">,</span> <span class="token string-literal singleline"><span class="token string">"</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span><span class="token expression">ioException<span class="token punctuation">.</span>message</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">"</span></span><span class="token punctuation">)</span>
<span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span>illegalStateException<span class="token operator">:</span> IllegalStateException<span class="token punctuation">)</span> <span class="token punctuation">{</span>
Log<span class="token punctuation">.</span><span class="token function">e</span><span class="token punctuation">(</span><span class="token string-literal singleline"><span class="token string">"Error getting records:"</span></span><span class="token punctuation">,</span> <span class="token string-literal singleline"><span class="token string">"</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span><span class="token expression">illegalStateException<span class="token punctuation">.</span>message</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">"</span></span><span class="token punctuation">)</span>
<span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span>e<span class="token operator">:</span> Exception<span class="token punctuation">)</span> <span class="token punctuation">{</span>
Log<span class="token punctuation">.</span><span class="token function">e</span><span class="token punctuation">(</span><span class="token string-literal singleline"><span class="token string">"Error getting records:"</span></span><span class="token punctuation">,</span> <span class="token string-literal singleline"><span class="token string">"</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span><span class="token expression">e<span class="token punctuation">.</span>message</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">"</span></span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>It's a suspend function that takes in a block that will be executed if the app has permissions. First, we check the permission situation from the Health Connect Manager and store the value to the <code>permissionsGranted</code> variable we defined in the previous blog post. </p>
<p>Then, if the permissions are granted, we try to execute the block we're passing in. There is also a somewhat granular exception handling - for this app, for now, it's just logging the errors, but this would be the place to handle errors in other ways, too. </p>
<p>Now that we have permission checks in place, we can read the Health Connect records and store them in the view model. </p>
<pre><code class="language-kotlin"><span class="token comment">// PeriodViewModel.kt </span>
<span class="token keyword">var</span> periods <span class="token keyword">by</span> mutableStateOf<span class="token operator"><</span>List<span class="token operator"><</span>MenstruationPeriodRecord<span class="token operator">></span><span class="token operator">></span><span class="token punctuation">(</span><span class="token function">emptyList</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
<span class="token keyword">fun</span> <span class="token function">getInitialRecords</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
viewModelScope<span class="token punctuation">.</span><span class="token function">launch</span> <span class="token punctuation">{</span>
tryWithPermissionsCheck <span class="token punctuation">{</span>
<span class="token function">getPeriodRecords</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token keyword">private</span> <span class="token keyword">fun</span> <span class="token function">getPeriodRecords</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
viewModelScope<span class="token punctuation">.</span><span class="token function">launch</span> <span class="token punctuation">{</span>
periods <span class="token operator">=</span> healthConnectManager<span class="token punctuation">.</span><span class="token function">readMenstruationRecords</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>In this block of code, we first define the <code>periods</code>-variable as a mutable state and list of <code>MenstruationPeriodRecord</code>. Then we define the <code>getInitialRecords</code>-function, which uses <code>tryWithPermissionsCheck</code> on calling <code>getPeriodRecords</code>, which calls <code>readMenstruationRecords</code> from the Health Connect Manager. Finally, it sets the records returned by this function to the <code>periods</code>-variable. </p>
<p>The final piece in the puzzle is the UI layer. The first bigger change is to change the type of data we're passing around: from the <code>Period</code>-data class we defined for the initial UI to <code>MenstruationPeriodRecord</code>. You can see all the changes required from the final commit, linked at the end of the blog post.</p>
<p>In the previous post, we left a couple of blocks with TODO-comments. Let's replace them with the actual data reading functions: </p>
<pre><code class="language-kotlin"><span class="token comment">// MainScreen.kt</span>
<span class="token keyword">val</span> permissionsLauncher <span class="token operator">=</span>
<span class="token function">rememberLauncherForActivityResult</span><span class="token punctuation">(</span>
viewModel<span class="token punctuation">.</span>permissionsLauncher
<span class="token punctuation">)</span> <span class="token punctuation">{</span>
viewModel<span class="token punctuation">.</span><span class="token function">getInitialRecords</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token function">LaunchedEffect</span><span class="token punctuation">(</span>Unit<span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>viewModel<span class="token punctuation">.</span>permissionsGranted<span class="token punctuation">)</span> <span class="token punctuation">{</span>
viewModel<span class="token punctuation">.</span><span class="token function">getInitialRecords</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
permissionsLauncher<span class="token punctuation">.</span><span class="token function">launch</span><span class="token punctuation">(</span>PERMISSIONS<span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>We call the <code>getInitialRecords</code> from the view model in these two places. What happens here is that when the user opens this activity, the <code>LaunchedEffect</code> is run. The app fetches the data from Health Connect if permissions are granted. If they're not, it calls the <code>launch</code>-method from the <code>permissionsLauncher</code> we defined to ask for permissions from the user. When the permissions are granted, it calls the <code>getInitialRecords</code>. </p>
<p>We also refactor the code to use <code>periods</code> from the view model instead of the hard-coded <code>periods</code>. </p>
<p>With these changes, we actually get data from the Health Connect! If you don't have any other apps writing this type of data, adding some records with the Health Connect Toolbox is a way to test that everything works as intended. </p>
<p>This is enough if you want to just read data from other apps. But if you want to add and modify data, then keep reading. </p>
<h3 id="write-records">Write Records</h3>
<p>For writing data to Health Connect, we'll take similar steps as when reading data: First, the Health Connect Manager, then the view model, and finally, the UI layer. </p>
<p>Let's start with the Health Connect Manager and add a function to write records:</p>
<pre><code class="language-kotlin"><span class="token comment">// HealthConnectManager.kt</span>
<span class="token keyword">suspend</span> <span class="token keyword">fun</span> <span class="token function">writeMenstruationRecords</span><span class="token punctuation">(</span>
menstruationPeriodRecord<span class="token operator">:</span> MenstruationPeriodRecord
<span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">val</span> records <span class="token operator">=</span> <span class="token function">listOf</span><span class="token punctuation">(</span>menstruationPeriodRecord<span class="token punctuation">)</span>
<span class="token keyword">try</span> <span class="token punctuation">{</span>
healthConnectClient<span class="token punctuation">.</span><span class="token function">insertRecords</span><span class="token punctuation">(</span>records<span class="token punctuation">)</span>
Toast<span class="token punctuation">.</span><span class="token function">makeText</span><span class="token punctuation">(</span>context<span class="token punctuation">,</span> <span class="token string-literal singleline"><span class="token string">"Successfully insert records"</span></span><span class="token punctuation">,</span> Toast<span class="token punctuation">.</span>LENGTH_SHORT<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">show</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span>e<span class="token operator">:</span> Exception<span class="token punctuation">)</span> <span class="token punctuation">{</span>
Toast<span class="token punctuation">.</span><span class="token function">makeText</span><span class="token punctuation">(</span>context<span class="token punctuation">,</span> e<span class="token punctuation">.</span>message<span class="token punctuation">.</span><span class="token function">toString</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> Toast<span class="token punctuation">.</span>LENGTH_SHORT<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">show</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
Log<span class="token punctuation">.</span><span class="token function">e</span><span class="token punctuation">(</span><span class="token string-literal singleline"><span class="token string">"Error"</span></span><span class="token punctuation">,</span> <span class="token string-literal singleline"><span class="token string">"Message: </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span><span class="token expression">e<span class="token punctuation">.</span>message</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">"</span></span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p><code>writeMenstruationRecords</code>-function takes in the record to write. As Health Connect Client's <code>insertRecords</code> takes in a list of records, we wrap our <code>menstruationPeriodRecord</code> in a list. </p>
<p>Within a <code>try - catch</code> block, we call the <code>insertRecords</code>-method, and if everything is successful, we'll show a toast message to the user. If there is an error, we show a toast message with the error and then log the error.</p>
<p>In the view model, we use the <code>tryWithPermissionsCheck</code>-function to check the permissions before writing data:</p>
<pre><code class="language-kotlin"><span class="token comment">// PeriodViewModel.kt</span>
<span class="token keyword">fun</span> <span class="token function">writeMenstruationRecord</span><span class="token punctuation">(</span>
menstruationPeriodRecord<span class="token operator">:</span> MenstruationPeriodRecord
<span class="token punctuation">)</span> <span class="token punctuation">{</span>
viewModelScope<span class="token punctuation">.</span><span class="token function">launch</span> <span class="token punctuation">{</span>
tryWithPermissionsCheck <span class="token punctuation">{</span>
healthConnectManager<span class="token punctuation">.</span><span class="token function">writeMenstruationRecords</span><span class="token punctuation">(</span>
menstruationPeriodRecord
<span class="token punctuation">)</span>
<span class="token function">getPeriodRecords</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>This function takes in the <code>MenstruationPeriodRecord</code> and writes it to Health Connect with the Health Connect Manager's <code>writeMenstruationRecords</code>. After that, it re-fetches the records from Health Connect to update the UI. I haven't found a way to observe the data yet, so it needs to be updated manually. </p>
<p>The next thing is to modify the UI. The date range picker dialog already takes in a function that is called when the user presses "Save" in the dialog. Let's edit it to allow writing data:</p>
<pre><code class="language-kotlin"><span class="token comment">// DateRangePickerDialog.kt</span>
<span class="token function">TextButton</span><span class="token punctuation">(</span>onClick <span class="token operator">=</span> <span class="token punctuation">{</span>
<span class="token keyword">val</span> startTime <span class="token operator">=</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>dateRangePickerState<span class="token punctuation">.</span>selectedStartDateMillis <span class="token operator">!=</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
Instant<span class="token punctuation">.</span><span class="token function">ofEpochMilli</span><span class="token punctuation">(</span>
dateRangePickerState<span class="token punctuation">.</span>selectedStartDateMillis<span class="token operator">!!</span>
<span class="token punctuation">)</span>
<span class="token punctuation">}</span> <span class="token keyword">else</span>
Instant<span class="token punctuation">.</span><span class="token function">now</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token keyword">val</span> endTime <span class="token operator">=</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>dateRangePickerState<span class="token punctuation">.</span>selectedEndDateMillis <span class="token operator">!=</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
Instant<span class="token punctuation">.</span><span class="token function">ofEpochMilli</span><span class="token punctuation">(</span>
dateRangePickerState<span class="token punctuation">.</span>selectedEndDateMillis<span class="token operator">!!</span>
<span class="token punctuation">)</span>
<span class="token punctuation">}</span> <span class="token keyword">else</span>
startTime<span class="token punctuation">.</span><span class="token function">plusSeconds</span><span class="token punctuation">(</span><span class="token number">60</span><span class="token punctuation">)</span> <span class="token comment">// This needs to be different from start time, but within the same day to work as intended on my code.</span>
<span class="token operator">..</span><span class="token punctuation">.</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token operator">..</span><span class="token punctuation">.</span> <span class="token punctuation">}</span>
</code></pre><p>Let's start unpacking the code. First, we need the start date and end date, and as Health Connect records take in <code>Instant</code>, we need to convert the milliseconds <code>dateRangePickerState</code> uses to <code>Instant</code>. We also need to check if the values are null - and if they are, then we set the start date to <code>Instant.now()</code> and the end date to be 60 seconds from the start date. This 60-second difference is because, for Health Connect menstruation period records, the end date can't be null, but it can't be equal to the start time either. </p>
<p>The app allows the user to not set the end date because if the menstruation is ongoing, there's no way to know when it ends. So, I've modified the code to check if the end time is the same date as the start time, and if so, then it's treated as null elsewhere in the code - like in the visualization. </p>
<p>Now, we have enough data to actually save the new period. The <code>DateRangePickerDialog</code> takes in a <code>selectedPeriod</code>, which is either an empty record with default values, or a record we're updating. We define this <code>updated</code> record with the start and end times inside the <code>onClick</code>-handler and call the <code>onConfirm</code>-function we pass to <code>DateRangePickerDialog</code>: </p>
<pre><code class="language-kotlin"><span class="token comment">// DateRangePickerDialog.kt</span>
<span class="token function">TextButton</span><span class="token punctuation">(</span>onClick <span class="token operator">=</span> <span class="token punctuation">{</span>
<span class="token operator">..</span><span class="token punctuation">.</span>
<span class="token keyword">val</span> updated <span class="token operator">=</span> <span class="token function">MenstruationPeriodRecord</span><span class="token punctuation">(</span>
startTime <span class="token operator">=</span> startTime<span class="token punctuation">,</span>
endTime <span class="token operator">=</span> endTime<span class="token punctuation">,</span>
startZoneOffset <span class="token operator">=</span> selectedPeriod<span class="token punctuation">.</span>startZoneOffset<span class="token punctuation">,</span>
endZoneOffset <span class="token operator">=</span> selectedPeriod<span class="token punctuation">.</span>endZoneOffset<span class="token punctuation">,</span>
metadata <span class="token operator">=</span> selectedPeriod<span class="token punctuation">.</span>metadata<span class="token punctuation">,</span>
<span class="token punctuation">)</span>
<span class="token function">onConfirm</span><span class="token punctuation">(</span>updated<span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token operator">..</span><span class="token punctuation">.</span> <span class="token punctuation">}</span>
</code></pre><p>Then, in <code>MainScreen</code>, we add a new function, <code>saveMenstruationPeriod</code> that calls the view model's function by the same name. We also modify the <code>onConfirm</code>-function a bit:</p>
<pre><code class="language-kotlin"><span class="token comment">// MainScreen.kt</span>
<span class="token keyword">fun</span> <span class="token function">saveMenstruationPeriod</span><span class="token punctuation">(</span>
menstruationPeriodRecord<span class="token operator">:</span> MenstruationPeriodRecord
<span class="token punctuation">)</span> <span class="token punctuation">{</span>
viewModel<span class="token punctuation">.</span><span class="token function">writeMenstruationRecord</span><span class="token punctuation">(</span>
menstruationPeriodRecord
<span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token operator">..</span><span class="token operator">..</span>
<span class="token function">DateRangePickerDialog</span><span class="token punctuation">(</span> <span class="token operator">..</span><span class="token punctuation">.</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> updatedSelectedPeriod <span class="token operator">-></span>
showDatePickerDialog <span class="token operator">=</span> <span class="token boolean">false</span>
selectedPeriod <span class="token operator">=</span> updatedSelectedPeriod
<span class="token function">saveMenstruationPeriod</span><span class="token punctuation">(</span>updatedSelectedPeriod<span class="token punctuation">)</span>
<span class="token punctuation">}</span>
</code></pre><p>Okay, now we've allowed the user to add new records. But what if the user inserts incorrect data and wants to edit the dates? They need to be able to edit the records, which we'll look at in the next blog post. </p>
<h2 id="wrapping-up">Wrapping Up</h2>
<p>In this blog post, we've looked at how to read and write data with Health Connect. You can find all the changes made to the code in the context of this blog post in this commit: <a href="https://github.com/eevajonnapanula/PeriodApp/commit/8e345160e1d88bf9cd6f1c082c13e72083b1d44f">Read and write data from/to Health Connect</a>.</p>
<p>In next week's blog post, we'll look into how to update and delete Health Connect data. So stay tuned for that!</p>
<h2 id="links-in-blog-post">Links in Blog Post</h2>
<ul>
<li><a href="https://developer.android.com/health-and-fitness/guides/health-connect/test/health-connect-toolbox">Health Connect Toolbox</a></li>
<li><a href="https://eevis.codes/blog/2024-01-12/exploring-health-connect-pt-1-setting-up-permissions/">Exploring Health Connect Pt. 1 - Setting Up Permissions</a></li>
<li><a href="https://github.com/eevajonnapanula/PeriodApp/commit/8e345160e1d88bf9cd6f1c082c13e72083b1d44f">Read and write data from/to Health Connect</a></li>
</ul>
</article>
Exploring Health Connect pt. 3 - Updating and Deleting Data2024-01-28T00:00:00.000Zhttps://eevis.codes/blog/2024-01-28/exploring-health-connect-pt-3-updating-and-deleting-data/
<nav id="breadcrumbs" aria-label="breadcrumbs">
<ol>
<li>
<a href="https://eevis.codes/blog/">Blog</a>
<span aria-hidden="true" class="breadcrumb-separator">/<span>
</span></span></li>
<li>Exploring Health Connect pt. 3 - Updating and Deleting Data</li><li>
</li></ol>
</nav>
<article class="blogPostLong">
<h1 id="/2024-01-28/exploring-health-connect-pt-3-updating-and-deleting-data">Exploring Health Connect pt. 3 - Updating and Deleting Data</h1>
<div class="reading-time-and-date">
<p>
Reading time: about 5 minutes
</p>
<span class="separator"></span>
<p>Published <time datetime="28th Jan, 2024">28th Jan, 2024</time></p>
</div>
<section class="likes-and-boosts">
<div>
<svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="boosts-title">
<title id="boosts-title">Boosts</title>
<path d="M7.19 4.35047C5.15 6.64047 3.75 9.93047 3.62 10.2405L0 8.69047L4.05 4.64047C4.52 4.17047 5.2 3.96047 5.86 4.09047L7.19 4.35047ZM9.17 15.0005C9.17 15.0005 12.91 13.4505 15.06 11.3005C20.46 5.90047 19.56 1.68047 19.27 0.730473C18.32 0.430473 14.1 -0.459527 8.7 4.94047C6.55 7.09047 5 10.8305 5 10.8305L9.17 15.0005ZM15.65 12.8105C13.36 14.8505 10.07 16.2505 9.76 16.3805L11.31 20.0005L15.36 15.9505C15.83 15.4805 16.04 14.8005 15.91 14.1405L15.65 12.8105ZM7 16.0005C7 16.8305 6.66 17.5805 6.12 18.1205C4.94 19.3005 0 20.0005 0 20.0005C0 20.0005 0.7 15.0605 1.88 13.8805C2.2988 13.4595 2.83325 13.1724 3.4155 13.0558C3.99775 12.9392 4.60153 12.9982 5.15017 13.2253C5.69882 13.4525 6.16758 13.8376 6.49694 14.3317C6.8263 14.8258 7.0014 15.4067 7 16.0005ZM11 7.00047C11 5.90047 11.9 5.00047 13 5.00047C14.1 5.00047 15 5.90047 15 7.00047C15 8.10047 14.1 9.00047 13 9.00047C11.9 9.00047 11 8.10047 11 7.00047Z" fill="#00696D"></path>
</svg>
0
</div>
<div>
<svg role="img" width="20" height="19" viewBox="0 0 20 19" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="likes-title">
<title id="likes-title">Likes</title>
<path d="M10 18.35L8.55 17.03C3.4 12.36 0 9.27 0 5.5C0 2.41 2.42 0 5.5 0C7.24 0 8.91 0.81 10 2.08C11.09 0.81 12.76 0 14.5 0C17.58 0 20 2.41 20 5.5C20 9.27 16.6 12.36 11.45 17.03L10 18.35Z"></path>
</svg>
0
</div>
</section>
<img class="cover-photo" src="https://images.ctfassets.net/mpqufjsy02zr/5wr3mkgcK5hLcXErUB4Ff/cb85384db27ef5095a663452e05dad9c/health-connect-permissions-3.png" alt="Exploring Health Connect- Updating and Deleting Data" />
<div class="blog-tags">
<a href="https://eevis.codes/tags/Health%20Connect"><span class="blog-tag turquoise">Health Connect</span></a> <a href="https://eevis.codes/tags/Android"><span class="blog-tag blue">Android</span></a> <a href="https://eevis.codes/tags/Kotlin"><span class="blog-tag turquoise">Kotlin</span></a>
</div>
<nav aria-label="Exploring Health Connect pt. 3 - Updating and Deleting Data Table of Contents" class="table-of-contents">
<h2>Table of Contents</h2>
<ol>
<li><a href="https://eevis.codes/blog/2024-01-28/exploring-health-connect-pt-3-updating-and-deleting-data/#update-records"> Update Records</a></li><li><a href="https://eevis.codes/blog/2024-01-28/exploring-health-connect-pt-3-updating-and-deleting-data/#delete-records"> Delete Records</a></li><li><a href="https://eevis.codes/blog/2024-01-28/exploring-health-connect-pt-3-updating-and-deleting-data/#wrapping-up"> Wrapping Up</a></li><li><a href="https://eevis.codes/blog/2024-01-28/exploring-health-connect-pt-3-updating-and-deleting-data/#links-in-blog-post"> Links in Blog Post</a></li>
</ol>
</nav>
<p>I've been building a period tracking app for myself, and my other goal has been to explore Health Connect. There are already two blog posts in this series:</p>
<ul>
<li><a href="https://eevis.codes/blog/2024-01-12/exploring-health-connect-pt-1-setting-up-permissions/">Exploring Health Connect Pt. 1 - Setting Up Permissions</a></li>
<li><a href="https://eevis.codes/blog/2024-01-19/exploring-health-connect-pt-2-reading-and-writing-data/">Exploring Health Connect Pt. 2 - Reading and Writing Data</a></li>
</ul>
<p>This is the third (and last) blog post of the MVP version of this period tracker. We'll look into updating and deleting Health Connect data. After changes in this blog post, the app will look like this:</p>
<video controls="" class="portrait-video">
<source src="https://videos.ctfassets.net/mpqufjsy02zr/3bI9reg5JMR37elhZZfKL3/e45f9e3ac9c0e148d54e461d6caeef14/period_app_rec.mp4" type="video/mp4" />
</video>
<p>So, let's start with allowing the user to update data. </p>
<h2 id="update-records">Update Records</h2>
<p>Updating records is pretty straightforward after the changes we made in the context of the last blog post. First, in Health Connect Manager, we add <code>updateMenstruationRecords</code>-function: </p>
<pre><code class="language-kotlin"><span class="token comment">// HealthConnectManager.kt</span>
<span class="token keyword">suspend</span> <span class="token keyword">fun</span> <span class="token function">updateMenstruationRecords</span><span class="token punctuation">(</span>
menstruationPeriodRecord<span class="token operator">:</span> MenstruationPeriodRecord
<span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">try</span> <span class="token punctuation">{</span>
healthConnectClient<span class="token punctuation">.</span><span class="token function">updateRecords</span><span class="token punctuation">(</span>
<span class="token function">listOf</span><span class="token punctuation">(</span>menstruationPeriodRecord<span class="token punctuation">)</span><span class="token punctuation">,</span>
<span class="token punctuation">)</span>
Toast<span class="token punctuation">.</span><span class="token function">makeText</span><span class="token punctuation">(</span>context<span class="token punctuation">,</span> <span class="token string-literal singleline"><span class="token string">"Successfully updated records"</span></span><span class="token punctuation">,</span> Toast<span class="token punctuation">.</span>LENGTH_SHORT<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">show</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span>e<span class="token operator">:</span> Exception<span class="token punctuation">)</span> <span class="token punctuation">{</span>
Toast<span class="token punctuation">.</span><span class="token function">makeText</span><span class="token punctuation">(</span>context<span class="token punctuation">,</span> e<span class="token punctuation">.</span>message<span class="token punctuation">.</span><span class="token function">toString</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> Toast<span class="token punctuation">.</span>LENGTH_SHORT<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">show</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
Log<span class="token punctuation">.</span><span class="token function">e</span><span class="token punctuation">(</span><span class="token string-literal singleline"><span class="token string">"Error"</span></span><span class="token punctuation">,</span> <span class="token string-literal singleline"><span class="token string">"Message: </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span><span class="token expression">e<span class="token punctuation">.</span>message</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">"</span></span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>It works the same way as writing - takes in a record, calls Health Connect Client's function <code>updateRecords</code> with a list of records, and then shows a toast message if an update is successful. </p>
<p>Then, in the view model, we add a function to call <code>updateMenstruationRecords</code> in Health Connect Manager:</p>
<pre><code class="language-kotlin"><span class="token comment">// PeriodViewModel.kt</span>
<span class="token keyword">fun</span> <span class="token function">updateMenstruationRecord</span><span class="token punctuation">(</span>
menstruationPeriodRecord<span class="token operator">:</span> MenstruationPeriodRecord
<span class="token punctuation">)</span> <span class="token punctuation">{</span>
viewModelScope<span class="token punctuation">.</span><span class="token function">launch</span> <span class="token punctuation">{</span>
tryWithPermissionsCheck <span class="token punctuation">{</span>
healthConnectManager
<span class="token punctuation">.</span><span class="token function">updateMenstruationRecords</span><span class="token punctuation">(</span>
menstruationPeriodRecord
<span class="token punctuation">)</span>
<span class="token function">getPeriodRecords</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>After a successful update, we re-read the records from Health Connect to update our UI as we did with the write-action.</p>
<p>Then, in the UI layer, we don't need to modify the <code>DateRangePickerDialog</code>, as the functionality already returns an updated record when the user presses the "Save"-button. However, we need to use different methods in <code>MainScreen</code> for writing and updating data. So, let's define an enum to represent whether we're updating or editing the dates:</p>
<pre><code class="language-kotlin"><span class="token comment">// MainScreen.kt</span>
<span class="token keyword">enum</span> <span class="token keyword">class</span> DatePickerAction <span class="token punctuation">{</span>
UPDATE<span class="token punctuation">,</span> INSERT
<span class="token punctuation">}</span>
</code></pre><p>We want to remember that value as a mutable state: </p>
<pre><code class="language-kotlin"><span class="token comment">// MainScreen.kt</span>
<span class="token keyword">var</span> datePickerAction <span class="token keyword">by</span> remember <span class="token punctuation">{</span>
<span class="token function">mutableStateOf</span><span class="token punctuation">(</span>DatePickerAction<span class="token punctuation">.</span>INSERT<span class="token punctuation">)</span>
<span class="token punctuation">}</span>
</code></pre><p>Then, we set the value for this <code>datePickerAction</code> when we're opening the date range picker dialog: When it's from the floating action button, we set it to <code>INSERT,</code> and when it's from the edit button in a period row, we set it to <code>UPDATE.</code></p>
<p>Then, in the <code>onConfirm</code>-block of the <code>DateRangePickerDialog</code>, we check the action and call the correct method:</p>
<pre><code class="language-kotlin"><span class="token comment">// MainScreen.kt</span>
<span class="token keyword">when</span> <span class="token punctuation">(</span>datePickerAction<span class="token punctuation">)</span> <span class="token punctuation">{</span>
DatePickerAction<span class="token punctuation">.</span>INSERT <span class="token operator">-></span>
<span class="token function">saveMenstruationPeriod</span><span class="token punctuation">(</span>updatedSelectedPeriod<span class="token punctuation">)</span>
DatePickerAction<span class="token punctuation">.</span>UPDATE <span class="token operator">-></span>
<span class="token function">updateMenstruationPeriod</span><span class="token punctuation">(</span>updatedSelectedPeriod<span class="token punctuation">)</span>
<span class="token punctuation">}</span>
</code></pre><p>And if you're wondering, the update method we're calling looks like this:</p>
<pre><code class="language-kotlin"><span class="token comment">// MainScreen.kt</span>
<span class="token keyword">fun</span> <span class="token function">updateMenstruationPeriod</span><span class="token punctuation">(</span>
menstruationPeriodRecord<span class="token operator">:</span> MenstruationPeriodRecord
<span class="token punctuation">)</span> <span class="token punctuation">{</span>
viewModel<span class="token punctuation">.</span><span class="token function">updateMenstruationRecord</span><span class="token punctuation">(</span>
menstruationPeriodRecord
<span class="token punctuation">)</span>
<span class="token punctuation">}</span>
</code></pre><p>Alright, after these changes, the user can update the dates. Yay! But we have one more action to add: Deleting a record. </p>
<h2 id="delete-records">Delete Records</h2>
<p>Once again, let's start with the Health Connect Manager and add a function there. For deleting records, it's a little bit more complex than for other actions. First, we want to get (possible) client record ID from the metadata of the menstruation period record we're passing in:</p>
<pre><code class="language-kotlin"><span class="token comment">// HealthConnectManager.kt </span>
<span class="token keyword">suspend</span> <span class="token keyword">fun</span> <span class="token function">deleteMenstruationRecords</span><span class="token punctuation">(</span>
menstruationPeriodRecord<span class="token operator">:</span> MenstruationPeriodRecord
<span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">val</span> clientRecordIdList <span class="token operator">=</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>menstruationPeriodRecord<span class="token punctuation">.</span>metadata<span class="token punctuation">.</span>clientRecordId <span class="token operator">!=</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token function">listOf</span><span class="token punctuation">(</span>
menstruationPeriodRecord<span class="token punctuation">.</span>metadata<span class="token punctuation">.</span>clientRecordId
<span class="token punctuation">)</span>
<span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
<span class="token function">emptyList</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token operator">..</span><span class="token punctuation">.</span>
<span class="token punctuation">}</span>
</code></pre><p>This is because the delete operation from Health Connect Client requires both client record IDs and IDs. Next, we pass in the variable we just defined and the id of the <code>MenstruationPeriodRecord</code> we're deleting. Otherwise, this function uses the same patterns as the others:</p>
<pre><code class="language-kotlin"><span class="token comment">// HealthConnectManager.kt</span>
<span class="token keyword">suspend</span> <span class="token keyword">fun</span> <span class="token function">deleteMenstruationRecords</span><span class="token punctuation">(</span>
menstruationPeriodRecord<span class="token operator">:</span> MenstruationPeriodRecord
<span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token operator">..</span><span class="token punctuation">.</span>
<span class="token keyword">try</span> <span class="token punctuation">{</span>
healthConnectClient<span class="token punctuation">.</span><span class="token function">deleteRecords</span><span class="token punctuation">(</span>
MenstruationPeriodRecord<span class="token operator">::</span><span class="token keyword">class</span><span class="token punctuation">,</span>
recordIdsList <span class="token operator">=</span> <span class="token function">listOf</span><span class="token punctuation">(</span>
menstruationPeriodRecord<span class="token punctuation">.</span>metadata<span class="token punctuation">.</span>id
<span class="token punctuation">)</span><span class="token punctuation">,</span>
clientRecordIdsList <span class="token operator">=</span> clientRecordIdList<span class="token punctuation">,</span>
<span class="token punctuation">)</span>
Toast<span class="token punctuation">.</span><span class="token function">makeText</span><span class="token punctuation">(</span>context<span class="token punctuation">,</span> <span class="token string-literal singleline"><span class="token string">"Successfully deleted records"</span></span><span class="token punctuation">,</span> Toast<span class="token punctuation">.</span>LENGTH_SHORT<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">show</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span>e<span class="token operator">:</span> Exception<span class="token punctuation">)</span> <span class="token punctuation">{</span>
Toast<span class="token punctuation">.</span><span class="token function">makeText</span><span class="token punctuation">(</span>context<span class="token punctuation">,</span> e<span class="token punctuation">.</span>message<span class="token punctuation">.</span><span class="token function">toString</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> Toast<span class="token punctuation">.</span>LENGTH_SHORT<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">show</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
Log<span class="token punctuation">.</span><span class="token function">e</span><span class="token punctuation">(</span><span class="token string-literal singleline"><span class="token string">"Error"</span></span><span class="token punctuation">,</span> <span class="token string-literal singleline"><span class="token string">"Message: </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span><span class="token expression">e<span class="token punctuation">.</span>message</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">"</span></span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>Next up is the view model. It's as straightforward as with the other operations:</p>
<pre><code class="language-kotlin"><span class="token comment">// PeriodViewModel.kt</span>
<span class="token keyword">fun</span> <span class="token function">deleteMenstruationRecord</span><span class="token punctuation">(</span>
menstruationPeriodRecord<span class="token operator">:</span> MenstruationPeriodRecord
<span class="token punctuation">)</span> <span class="token punctuation">{</span>
viewModelScope<span class="token punctuation">.</span><span class="token function">launch</span> <span class="token punctuation">{</span>
tryWithPermissionsCheck <span class="token punctuation">{</span>
healthConnectManager<span class="token punctuation">.</span><span class="token function">deleteMenstruationRecords</span><span class="token punctuation">(</span>
menstruationPeriodRecord
<span class="token punctuation">)</span>
<span class="token function">getPeriodRecords</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>So, once again, we call the <code>deleteMenstruationRecords</code> from Health Connect Manager after checking if the app has all the permissions. After that, we re-read the records after deleting the item.</p>
<p>We'll need to do a couple of things on the UI side. First, we want to change the edit button in the <code>PeriodRow</code> to a menu with two actions: Edit and Delete, as seen in the picture: </p>
<img src="https://images.ctfassets.net/mpqufjsy02zr/2nksagTzmwl702GWO5g66i/1a518674ebdd63b4b6146aaacc780e71/Frame_3.png" alt="On the top, there's a picture of the old implementation with the period card element having a edit-button with pen-icon at the end of the row. Under it, there are three arrows pointing down to a period card element, that has three dots instead of the pen icon, and opened menu that has Edit- and Delete-actions." class="portrait-img" />
<p>As this blog post is about Health Connect (and getting rather long), I'm leaving out the details of the UI change, but if you're interested, the change is reflected in the final commit. </p>
<p>To complete the deletion, we need to pass the action to execute when the user presses "Delete". This, again, is pretty similar to what we did with other options: Defining a method and then calling it in the correct place in the UI:</p>
<pre><code class="language-kotlin"><span class="token comment">// MainScreen.kt</span>
<span class="token keyword">fun</span> <span class="token function">deleteMenstruationPeriod</span><span class="token punctuation">(</span>
menstruationPeriodRecord<span class="token operator">:</span> MenstruationPeriodRecord
<span class="token punctuation">)</span> <span class="token punctuation">{</span>
viewModel<span class="token punctuation">.</span><span class="token function">deleteMenstruationRecord</span><span class="token punctuation">(</span>
menstruationPeriodRecord
<span class="token punctuation">)</span>
<span class="token punctuation">}</span>
</code></pre><p>and </p>
<pre><code class="language-kotlin"><span class="token comment">// MainScreen.kt</span>
<span class="token function">PeriodRow</span><span class="token punctuation">(</span>
<span class="token operator">..</span><span class="token punctuation">.</span>
onDeleteIconClick <span class="token operator">=</span> <span class="token punctuation">{</span>
<span class="token function">deleteMenstruationPeriod</span><span class="token punctuation">(</span>it<span class="token punctuation">)</span>
<span class="token operator">..</span><span class="token punctuation">.</span>
<span class="token punctuation">}</span>
<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token operator">..</span><span class="token punctuation">.</span> <span class="token punctuation">}</span>
</code></pre><p>With these changes, the user can delete the period. From a UX perspective, it would be good to add a dialog to ask if the user really wants to delete that menstruation record, but for the sake of this blog post being straightforward, I decided to leave implementing that to some later moment.</p>
<h2 id="wrapping-up">Wrapping Up</h2>
<p>In this blog post, we've looked at how to update and delete data from Health Connect. You can find all the changes made to the code in the context of this blog post in this commit: <a href="https://github.com/eevajonnapanula/PeriodApp/commit/db3f267fc31cf259c101aa30f003eb51cdf34d53">Update and delete data from Health Connect</a>.</p>
<p>I'm planning to develop this app further, including things like adding menstruation flow to the mix (that is, how much you bleed when menstruating?). From a development perspective, this will be interesting because it's another type of record, and working with aggregated data adds more complexity to the code. I won't promise to publish that blog post next week, but sometime in the near-ish future!</p>
<h2 id="links-in-blog-post">Links in Blog Post</h2>
<ul>
<li><a href="https://eevis.codes/blog/2024-01-12/exploring-health-connect-pt-1-setting-up-permissions/">Exploring Health Connect Pt. 1 - Setting Up Permissions</a></li>
<li><a href="https://eevis.codes/blog/2024-01-19/exploring-health-connect-pt-2-reading-and-writing-data/">Exploring Health Connect Pt. 2 - Reading and Writing Data</a></li>
<li><a href="https://github.com/eevajonnapanula/PeriodApp/commit/db3f267fc31cf259c101aa30f003eb51cdf34d53">Update and delete data from Health Connect</a></li>
</ul>
</article>
Font Size Considerations for Accessibility2024-02-02T00:00:00.000Zhttps://eevis.codes/blog/2024-02-02/font-size-considerations-for-accessibility/
<nav id="breadcrumbs" aria-label="breadcrumbs">
<ol>
<li>
<a href="https://eevis.codes/blog/">Blog</a>
<span aria-hidden="true" class="breadcrumb-separator">/<span>
</span></span></li>
<li>Font Size Considerations for Accessibility</li><li>
</li></ol>
</nav>
<article class="blogPostLong">
<h1 id="/2024-02-02/font-size-considerations-for-accessibility">Font Size Considerations for Accessibility</h1>
<div class="reading-time-and-date">
<p>
Reading time: about 5 minutes
</p>
<span class="separator"></span>
<p>Published <time datetime="2nd Feb, 2024">2nd Feb, 2024</time></p>
</div>
<section class="likes-and-boosts">
<div>
<svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="boosts-title">
<title id="boosts-title">Boosts</title>
<path d="M7.19 4.35047C5.15 6.64047 3.75 9.93047 3.62 10.2405L0 8.69047L4.05 4.64047C4.52 4.17047 5.2 3.96047 5.86 4.09047L7.19 4.35047ZM9.17 15.0005C9.17 15.0005 12.91 13.4505 15.06 11.3005C20.46 5.90047 19.56 1.68047 19.27 0.730473C18.32 0.430473 14.1 -0.459527 8.7 4.94047C6.55 7.09047 5 10.8305 5 10.8305L9.17 15.0005ZM15.65 12.8105C13.36 14.8505 10.07 16.2505 9.76 16.3805L11.31 20.0005L15.36 15.9505C15.83 15.4805 16.04 14.8005 15.91 14.1405L15.65 12.8105ZM7 16.0005C7 16.8305 6.66 17.5805 6.12 18.1205C4.94 19.3005 0 20.0005 0 20.0005C0 20.0005 0.7 15.0605 1.88 13.8805C2.2988 13.4595 2.83325 13.1724 3.4155 13.0558C3.99775 12.9392 4.60153 12.9982 5.15017 13.2253C5.69882 13.4525 6.16758 13.8376 6.49694 14.3317C6.8263 14.8258 7.0014 15.4067 7 16.0005ZM11 7.00047C11 5.90047 11.9 5.00047 13 5.00047C14.1 5.00047 15 5.90047 15 7.00047C15 8.10047 14.1 9.00047 13 9.00047C11.9 9.00047 11 8.10047 11 7.00047Z" fill="#00696D"></path>
</svg>
0
</div>
<div>
<svg role="img" width="20" height="19" viewBox="0 0 20 19" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="likes-title">
<title id="likes-title">Likes</title>
<path d="M10 18.35L8.55 17.03C3.4 12.36 0 9.27 0 5.5C0 2.41 2.42 0 5.5 0C7.24 0 8.91 0.81 10 2.08C11.09 0.81 12.76 0 14.5 0C17.58 0 20 2.41 20 5.5C20 9.27 16.6 12.36 11.45 17.03L10 18.35Z"></path>
</svg>
0
</div>
</section>
<img class="cover-photo" src="https://images.ctfassets.net/mpqufjsy02zr/59uABnSGvwV4NZVQtWQkd0/90e2111584d61a6c6649a030db552af0/font-size__2_.png" alt="Font Size Considerations for Accessibility" />
<div class="blog-tags">
<a href="https://eevis.codes/tags/a11y"><span class="blog-tag turquoise">a11y</span></a> <a href="https://eevis.codes/tags/Kotlin"><span class="blog-tag turquoise">Kotlin</span></a> <a href="https://eevis.codes/tags/Android"><span class="blog-tag blue">Android</span></a>
</div>
<nav aria-label="Font Size Considerations for Accessibility Table of Contents" class="table-of-contents">
<h2>Table of Contents</h2>
<ol>
<li><a href="https://eevis.codes/blog/2024-02-02/font-size-considerations-for-accessibility/#font-size"> Font Size</a></li><li><a href="https://eevis.codes/blog/2024-02-02/font-size-considerations-for-accessibility/#testing-font-size"> Testing Font Size</a></li><li><a href="https://eevis.codes/blog/2024-02-02/font-size-considerations-for-accessibility/#fixing-font-size"> Fixing Font Size</a></li><li><a href="https://eevis.codes/blog/2024-02-02/font-size-considerations-for-accessibility/#wrapping-up"> Wrapping Up</a></li><li><a href="https://eevis.codes/blog/2024-02-02/font-size-considerations-for-accessibility/#links-in-the-blog-post"> Links in the Blog Post</a></li>
</ol>
</nav>
<p>Discussions about accessibility, especially in software development, often center around screen reader accessibility. With that, I mean that if accessibility is considered, it's mostly about, for example, content descriptions, setting correct semantics, and then leaving it there. </p>
<p>Don't get me wrong, it is important. But the problems arise when only taking these actions for accessibility and calling it done. In some apps, those actions also bring many good things for other users. But in others, many issues that create barriers to usage can be missed if the attention is only on screen reader-related accessibility.</p>
<p>In this and a couple of future blog posts, I'll share some things you can check to find accessibility issues, and suggest ideas for fixing them. As you might guess, these are not related to screen reader accessibility. This first blog post is about font sizing.</p>
<p>By the way, I'm currently writing my master's thesis, and its goal is to create an accessibility checklist for Android devs to help create more accessible Android apps. The initial checklist is live, and I'm collecting feedback, so if you want to help me, check the <a href="https://android-a11y-checks.netlify.app/checks">Android Accessibility Checklist</a> and then answer a couple of questions in this <a href="https://forms.gle/FeY5VUBEX2P728C38">Google Form</a>. I'm really, really thankful for anyone participating!</p>
<p>Alright, let's get started. </p>
<h2 id="font-size">Font Size</h2>
<p>One interesting thing related to font sizing is that about 25% of Android users increase the font size. (source: <a href="https://appt.org/en/stats">Appt.org</a>) So this is a big group of our users! Sometimes, accessibility is dismissed because it's said to be something that concerns only a small group of users. A quarter of the user base is already a considerable amount of users - actually, it can be bigger than, for example, some language your app supports. </p>
<p>But if the user increases font size, it might cause problems with, for example, text overflowing or being clipped. We will explore some of these issues later in this blog post, but first, let's talk about how to test your app for different font sizes.</p>
<h2 id="testing-font-size">Testing Font Size</h2>
<p>For testing the font size, you have at least two options: Using a phone (real device or emulator) or Compose previews. Let's look at each of them in the following subsections. </p>
<h3 id="on-phone">On Phone</h3>
<p>You can test your app by changing the font size in your Accessibility settings. It's most likely located in <em>Settings > Accessibility > Display size and text > Font Size</em>. Turn the font size to the biggest option.</p>
<p>After increasing the font size, go through your app. Notice anything that does not work? Some text is unreadable? You've found the problems. The next section will contain some suggestions on how to fix these issues.</p>
<h3 id="with-compose-previews">With Compose Previews</h3>
<p>You can also test how different font sizes behave with Compose's previews. If you're working pre-Compose 1.6, you'll need to define font scales explicitly. You can do that via the <code>fontScale</code>-attribute:</p>
<pre><code class="language-kotlin"><span class="token annotation builtin">@Preview</span><span class="token punctuation">(</span>fontScale <span class="token operator">=</span> <span class="token number">1.8f</span><span class="token punctuation">)</span>
<span class="token annotation builtin">@Composable</span>
<span class="token keyword">fun</span> <span class="token function">ComponentPreview</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token operator">..</span><span class="token punctuation">.</span>
<span class="token punctuation">}</span>
</code></pre><p>Compose 1.6 brings something handy: Multipreview API templates, specifically <code>@PreviewFontScale</code>. </p>
<pre><code class="language-kotlin"><span class="token annotation builtin">@PreviewFontScale</span>
<span class="token annotation builtin">@Preview</span>
<span class="token annotation builtin">@Composable</span>
<span class="token keyword">fun</span> <span class="token function">ComponentPreview</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token operator">..</span><span class="token punctuation">.</span>
<span class="token punctuation">}</span>
</code></pre><p>In the picture below, you can see how it adds previews for all the configured font sizes (85%, 100%, 115%, 130%, 150%, 180%, and 200%) for my graph example app:</p>
<p><img src="https://images.ctfassets.net/mpqufjsy02zr/5gFCvidX64iNrIV8jrAdjD/663fe2fa3d2f848f1ac119589998b69f/Screenshot_2024-02-01_at_6.27.25.png" alt="Preview of 8 screens with a line graph with different font scales." /></p>
<h2 id="fixing-font-size">Fixing Font Size</h2>
<p>Okay, you tested your app, and you've found a problem. What to do to fix it? I'm going to use one of my favorite answers: It depends. I'll list a couple of issues and how to fix them. </p>
<h3 id="problem-font-itself-is-not-scalable">Problem: Font Itself is not Scalable</h3>
<p>If the font itself doesn't scale, one reason could be that you haven't configured the font size correctly. </p>
<p>This "not configured correctly" mainly means that the font sizes are something other than <code>sp</code>, scalable pixels. Check your text styling, and if it's something other than <code>sp</code> (for example, <code>dp</code>), change it to <code>sp.</code> That should fix the problem of the font itself not scaling.</p>
<h3 id="problem-text-cuts-out">Problem: Text Cuts Out</h3>
<p>Another possible problem is that you've defined the components wrapping the text as not flexible enough to accommodate bigger or longer texts. This usually leads to text either overflowing or being clipped - both making the user interface harder to use. </p>
<p>One place where this often happens is graphs. It's not always straightforward to accommodate all font sizes when you use something like Canvas that is not so responsive to begin with. And if you can't, you should think about alternative ways to provide that data (you probably should anyway, because not everyone enjoys, or even understands, graphs).</p>
<p>Here's one example from a graph example app, zoomed to 200%: </p>
<img src="https://images.ctfassets.net/mpqufjsy02zr/1k1KrK0A1bJQFsAwGVvjg9/0e915e6a1b011143441b59bee71f1a6d/Screenshot_2024-02-01_at_6.28.07.png" alt="A screen with line graph. X and y-axis labels of the graph cut off, not showing them fully." class="portrait-img" />
<p>The picture shows how x- and y-axis labels cut off because there is not enough space for them when the font size increases. One way to fix this is to add some room for growth for the labels. You can even make the paddings (or whichever thing you'll need to wrangle here) dynamic based on the font scale. </p>
<p>You can access the user's font scale value in Compose through their configuration. To be more precise, with <code>LocalConfiguration.current.fontScale</code>. It is a float value - for 100% font scale, it's 1, and for 200% 2. </p>
<p>The exact code for fixing the issues varies case by case, but here's what I did on my graph (I will write a more extensive blog post about this later): </p>
<pre><code class="language-kotlin"><span class="token comment">// First, get the font scale</span>
<span class="token keyword">val</span> fontScale <span class="token operator">=</span> LocalConfiguration<span class="token punctuation">.</span>current<span class="token punctuation">.</span>fontScale
<span class="token comment">// Second, use it to scale the padding:</span>
<span class="token function">Canvas</span><span class="token punctuation">(</span>
modifier <span class="token operator">=</span> Modifier
<span class="token punctuation">.</span><span class="token function">padding</span><span class="token punctuation">(</span>
bottom <span class="token operator">=</span> GraphComponent<span class="token punctuation">.</span>innerPadding <span class="token operator">*</span> fontScale<span class="token punctuation">,</span>
start <span class="token operator">=</span> GraphComponent<span class="token punctuation">.</span>innerPadding <span class="token operator">*</span> fontScale<span class="token punctuation">,</span>
end <span class="token operator">=</span> GraphComponent<span class="token punctuation">.</span>innerPadding <span class="token operator">/</span> <span class="token number">2</span><span class="token punctuation">,</span>
<span class="token punctuation">)</span>
<span class="token operator">..</span><span class="token punctuation">.</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token operator">..</span><span class="token punctuation">.</span> <span class="token punctuation">}</span>
</code></pre><p>This way, I get some breathing room for the axis labels. Here's the same 200% font scale picture after the changes:</p>
<img src="https://images.ctfassets.net/mpqufjsy02zr/10ZhEbUcMzC3ihuKB5FZMP/c71c3fbdfc59aad1382082f1c1259dbb/Screenshot_2024-02-01_at_6.46.18.png" alt="A screen with a line graph. The y- and x-axis labels have enough space to be visible, and they're not cut off. The buttons under the graph don't fit well, and the Next year button doesn't show the arrow icon." class="portrait-img" />
<p>I'd be happy to know if you've found other ways to solve these issues, especially with graphs and Canvas!</p>
<h3 id="problem-layout-is-not-responsive">Problem: Layout is not Responsive</h3>
<p>You might have noticed another problem related to font size in the previous pictures: When font size increases, buttons under the graph don't accommodate well. This is a common problem with layouts that must fit two or more items in a row. </p>
<p>One way to fix this is to allow flexibility on items. So, if these items fit on one row, they stay like that, but if not, they should be on two (or more) rows. Compose 1.5.0 added a nice, although experimental, layout composable called <a href="https://developer.android.com/reference/kotlin/androidx/compose/foundation/layout/package-summary#FlowRow(androidx.compose.ui.Modifier,androidx.compose.foundation.layout.Arrangement.Horizontal,androidx.compose.foundation.layout.Arrangement.Vertical,kotlin.Int,kotlin.Function1)"><code>FlowRow</code></a>, which does what I described. </p>
<p>When I change the current implementation to use <code>FlowRow</code> (So, just <code>Row</code> -> <code>FlowRow</code>), the buttons align nicely with 200% font size:</p>
<img src="https://images.ctfassets.net/mpqufjsy02zr/6hEAh5HqYGzzOvQYKYL27W/d94396ccf8acf3d6f5eee09aab06615d/Screenshot_2024-02-01_at_6.57.04.png" alt="A screen with a line graph. Buttons under the graph are on two lines, and the text for both of the buttons is fully visible." class="portrait-img" />
<h2 id="wrapping-up">Wrapping Up</h2>
<p>In this blog post, we've looked into font size and what problems might appear if different font sizes are not tested and accommodated. I've provided some examples of these problems and how to fix them. </p>
<p>Have you encountered any of these problems? Or have you gotten user feedback about font sizes? Do you have alternative ways to fix the issues? I'd love to hear, so please share them!</p>
<h2 id="links-in-the-blog-post">Links in the Blog Post</h2>
<ul>
<li><a href="https://appt.org/en/stats">Appt.org</a></li>
<li><a href="https://android-a11y-checks.netlify.app/checks">Android Accessibility Checklist</a> </li>
<li><a href="https://forms.gle/FeY5VUBEX2P728C38">Google Form</a></li>
<li><a href="https://developer.android.com/reference/kotlin/androidx/compose/foundation/layout/package-summary#FlowRow(androidx.compose.ui.Modifier,androidx.compose.foundation.layout.Arrangement.Horizontal,androidx.compose.foundation.layout.Arrangement.Vertical,kotlin.Int,kotlin.Function1)"><code>FlowRow</code></a></li>
</ul>
</article>
Testing with Accessibility Scanner2024-02-16T00:00:00.000Zhttps://eevis.codes/blog/2024-02-16/testing-with-accessibility-scanner/
<nav id="breadcrumbs" aria-label="breadcrumbs">
<ol>
<li>
<a href="https://eevis.codes/blog/">Blog</a>
<span aria-hidden="true" class="breadcrumb-separator">/<span>
</span></span></li>
<li>Testing with Accessibility Scanner</li><li>
</li></ol>
</nav>
<article class="blogPostLong">
<h1 id="/2024-02-16/testing-with-accessibility-scanner">Testing with Accessibility Scanner</h1>
<div class="reading-time-and-date">
<p>
Reading time: about 4 minutes
</p>
<span class="separator"></span>
<p>Published <time datetime="16th Feb, 2024">16th Feb, 2024</time></p>
</div>
<section class="likes-and-boosts">
<div>
<svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="boosts-title">
<title id="boosts-title">Boosts</title>
<path d="M7.19 4.35047C5.15 6.64047 3.75 9.93047 3.62 10.2405L0 8.69047L4.05 4.64047C4.52 4.17047 5.2 3.96047 5.86 4.09047L7.19 4.35047ZM9.17 15.0005C9.17 15.0005 12.91 13.4505 15.06 11.3005C20.46 5.90047 19.56 1.68047 19.27 0.730473C18.32 0.430473 14.1 -0.459527 8.7 4.94047C6.55 7.09047 5 10.8305 5 10.8305L9.17 15.0005ZM15.65 12.8105C13.36 14.8505 10.07 16.2505 9.76 16.3805L11.31 20.0005L15.36 15.9505C15.83 15.4805 16.04 14.8005 15.91 14.1405L15.65 12.8105ZM7 16.0005C7 16.8305 6.66 17.5805 6.12 18.1205C4.94 19.3005 0 20.0005 0 20.0005C0 20.0005 0.7 15.0605 1.88 13.8805C2.2988 13.4595 2.83325 13.1724 3.4155 13.0558C3.99775 12.9392 4.60153 12.9982 5.15017 13.2253C5.69882 13.4525 6.16758 13.8376 6.49694 14.3317C6.8263 14.8258 7.0014 15.4067 7 16.0005ZM11 7.00047C11 5.90047 11.9 5.00047 13 5.00047C14.1 5.00047 15 5.90047 15 7.00047C15 8.10047 14.1 9.00047 13 9.00047C11.9 9.00047 11 8.10047 11 7.00047Z" fill="#00696D"></path>
</svg>
0
</div>
<div>
<svg role="img" width="20" height="19" viewBox="0 0 20 19" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="likes-title">
<title id="likes-title">Likes</title>
<path d="M10 18.35L8.55 17.03C3.4 12.36 0 9.27 0 5.5C0 2.41 2.42 0 5.5 0C7.24 0 8.91 0.81 10 2.08C11.09 0.81 12.76 0 14.5 0C17.58 0 20 2.41 20 5.5C20 9.27 16.6 12.36 11.45 17.03L10 18.35Z"></path>
</svg>
0
</div>
</section>
<img class="cover-photo" src="https://images.ctfassets.net/mpqufjsy02zr/7eSZtcL2l2O0hGa0ibdtdH/3b0c80f162b27ac7e721f02861a48b49/a11y-scanner.png" alt="Testing with Accessibility Scanner" />
<div class="blog-tags">
<a href="https://eevis.codes/tags/a11y"><span class="blog-tag turquoise">a11y</span></a> <a href="https://eevis.codes/tags/Android"><span class="blog-tag blue">Android</span></a>
</div>
<nav aria-label="Testing with Accessibility Scanner Table of Contents" class="table-of-contents">
<h2>Table of Contents</h2>
<ol>
<li><a href="https://eevis.codes/blog/2024-02-16/testing-with-accessibility-scanner/#what-is-accessibility-scanner"> What Is Accessibility Scanner</a></li><li><a href="https://eevis.codes/blog/2024-02-16/testing-with-accessibility-scanner/#using-accessibility-scanner"> Using Accessibility Scanner</a></li><li><a href="https://eevis.codes/blog/2024-02-16/testing-with-accessibility-scanner/#wrapping-up"> Wrapping Up</a></li><li><a href="https://eevis.codes/blog/2024-02-16/testing-with-accessibility-scanner/#links-in-the-blog-post"> Links in the Blog Post</a></li>
</ol>
</nav>
<p>When I talk about accessibility with Android developers, I often hear that some easy-to-use tests should automatically catch every possible accessibility issue. And I get it. That would make the whole topic of accessibility so much easier and straightforward.</p>
<p>But the truth is that automated testing only catches some things. Studies suggest that the maximum percentage of caught accessibility issues is 40-50%. Deque, the company that develops the aXe accessibility tool, claims that the number is 57% in their <a href="https://www.deque.com/blog/automated-testing-study-identifies-57-percent-of-digital-accessibility-issues/">study of automated accessibility tools</a>. These studies have been performed with websites, but based on my experience, I have no reason to believe that the number would be higher on Android. So manual testing is always needed.</p>
<p>But for that 40-57% (or whatever the exact number is on Android accessibility testing), there are some tools to use. One of them is Accessibility Scanner, and in this blog post, I'll discuss how to test your app with it. Let's start with what it is.</p>
<h2 id="what-is-accessibility-scanner">What Is Accessibility Scanner</h2>
<p>Accessibility Scanner is a helpful tool for semi-automated accessibility testing. It's an app which can catch issues from the following categories: </p>
<ul>
<li>Content labels</li>
<li>Touch target size</li>
<li>Clickable items</li>
<li>Text and image contrast</li>
</ul>
<p>It also suggests how to fix some of its findings and provides more info about the issues. However, it does not find every possible accessibility issue and thus does not guarantee the accessibility of your app. Manual testing is still needed. I keep saying this because I've seen these claims so many times that "Well, our test automation didn't find any accessibility issues, so our app is accessible". </p>
<p>One great thing is that you can use Accessibility Scanner with any Android app - it can be a native Android app or built with cross-platform technologies such as Flutter or React Native. Or it can even be a PWA - Progressive Web App. </p>
<h2 id="using-accessibility-scanner">Using Accessibility Scanner</h2>
<h3 id="installation">Installation</h3>
<p>To use the Accessibility Scanner, you must first download it from Google Play: <a href="https://play.google.com/store/apps/details?id=com.google.android.apps.accessibility.auditor">Accessibility Scanner</a>. Once downloaded, you can turn it on from Accessibility settings (Settings -> Accessibility -> Accessibility Scanner -> Use service). This action adds a button overlay on your screen, and it's the access point to using the Accessibility Scanner. </p>
<h3 id="usage">Usage</h3>
<p>Accessibility Scanner provides two options: Scan one screen or record multiple screens. Navigate to your app, then press the Accessibility Scanner button on the screen. It opens a menu where you can choose to record, take a snapshot (one screen), or turn the Accessibility scanner off.</p>
<p><img src="https://images.ctfassets.net/mpqufjsy02zr/6mYFqb2UgfJ3cm2AzMzkBs/a795a502f7e39f49064ae254f48c9704/Accessibility_Scanner.png" alt="Two screens in a row; the first has a light blue rectangle button with a checkmark overlaying the content of the screen, and the other has the menu opened with the following options: Record, Snapshot, Turn off and Collapse." /></p>
<p>Press the record option to record a flow or multiple screens. Then, navigate through the screens. If your phone has a vibration turned on, you should feel a slight vibration every time the app takes a screenshot. </p>
<p>I've noticed that sometimes I need to go back and forth to get a screenshot from some screens. I don't know why this is, but navigating to another screen and back has usually worked for me - if not the first time, then the second time. </p>
<p>You can end the recording by pressing the same button that has now transitioned into a stop button. If it's not visible on the screen, then you can, for example, open the Quick settings (slide them down from the top of the screen). The button should re-appear. </p>
<h3 id="results">Results</h3>
<p>Okay, now you've either taken a snapshot or recorded a flow. The next step is to see the results and interpret them. </p>
<p>The Accessibility Scanner provides an overview of the screens: </p>
<img src="https://images.ctfassets.net/mpqufjsy02zr/5Fu08he32S4JbeLb5sxkBY/6b88bf8c8d8be60a92a93d8bf6a8a186/Screenshot_20240214_064950_1.png" alt="Accessibility Scanner's screen where all the screenshot it took from the recording are displayed at the top on a row, and one selected screen is displayed under that row. The selected screen takes the most of the available screen space." class="portrait-img" />
<p>You can navigate through the screens and see possible problems highlighted. If you prefer seeing the suggestions in a list view, you can find it from the top right corner, under the list icon:</p>
<img src="https://images.ctfassets.net/mpqufjsy02zr/5Y6liIaOlKKn98xBT6Azi6/e6b344a3370446e0d327a9e89edebb36/List-icon.png" alt="Accessibility Scanner's screen, where the toolbar's right side's list icon has been highlighted with a pink rectangle and an arrow pointing at it." class="portrait-img" />
<p>Found issues are grouped either by screen or by category. When clicking the element on the screen, it displays a screenshot of the problematic element:</p>
<img src="https://images.ctfassets.net/mpqufjsy02zr/1ABqVNwLetcMysOzKs8IWk/797534d297f8b00a4fbf925e1fe8ab29/Screenshot_20240214_065050.png" alt="An issue with a disabled Save button having poor color contrast zoomed in on the button. Under the screenshot, there are details about the issue." class="portrait-img" />
<p>It usually also provides ideas on how to fix the issue. You can read more about different issues and their possible causes and fixes from the materials mentioned in the following section.</p>
<h3 id="learn-more-about-accessibility-scanner">Learn More About Accessibility Scanner</h3>
<p>Google has created materials to learn more about the Accessibility Scanner. There are two good text-based resources: </p>
<ul>
<li><a href="https://support.google.com/accessibility/android/answer/6376570?hl=en&sjid=15077242903809662356-EU">Get Started with Accessibility Scanner</a></li>
<li><a href="https://support.google.com/accessibility/android/answer/6376559?hl=en&sjid=15077242903809662356-EU">Accessibility Scanner Results</a></li>
</ul>
<p>And if you prefer watching a video, they also have one on YouTube: <a href="https://www.youtube.com/watch?v=i1gMzQv0hWU">Accessibility Scanner - Accessibility on Android</a>.</p>
<h2 id="wrapping-up">Wrapping Up</h2>
<p>In this blog post, we've discussed Accessibility Scanner, a tool for testing some parts of Android apps' accessibility. While it does not guarantee fully accessible apps, it is a great tool for finding low-hanging issues. </p>
<p>Have you tried Accessibility Scanner? Or is it in regular use for you? If you've tried it, what was the most interesting thing you found? </p>
<h2 id="links-in-the-blog-post">Links in the Blog Post</h2>
<ul>
<li><a href="https://www.deque.com/blog/automated-testing-study-identifies-57-percent-of-digital-accessibility-issues/">Study of automated accessibility tools</a></li>
<li><a href="https://play.google.com/store/apps/details?id=com.google.android.apps.accessibility.auditor">Accessibility Scanner</a></li>
<li><a href="https://support.google.com/accessibility/android/answer/6376570?hl=en&sjid=15077242903809662356-EU">Get Started with Accessibility Scanner</a></li>
<li><a href="https://support.google.com/accessibility/android/answer/6376559?hl=en&sjid=15077242903809662356-EU">Accessibility Scanner Results</a></li>
<li><a href="https://www.youtube.com/watch?v=i1gMzQv0hWU">Accessibility Scanner - Accessibility on Android</a></li>
</ul>
</article>
Another Year Of Being in Gender Minority in Tech2024-03-01T00:00:00.000Zhttps://eevis.codes/blog/2024-03-01/another-year-of-being-in-gender-minority-in-tech/
<nav id="breadcrumbs" aria-label="breadcrumbs">
<ol>
<li>
<a href="https://eevis.codes/blog/">Blog</a>
<span aria-hidden="true" class="breadcrumb-separator">/<span>
</span></span></li>
<li>Another Year Of Being in Gender Minority in Tech</li><li>
</li></ol>
</nav>
<article class="blogPostLong">
<h1 id="/2024-03-01/another-year-of-being-in-gender-minority-in-tech">Another Year Of Being in Gender Minority in Tech</h1>
<div class="reading-time-and-date">
<p>
Reading time: about 6 minutes
</p>
<span class="separator"></span>
<p>Published <time datetime="1st Mar, 2024">1st Mar, 2024</time></p>
</div>
<section class="likes-and-boosts">
<div>
<svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="boosts-title">
<title id="boosts-title">Boosts</title>
<path d="M7.19 4.35047C5.15 6.64047 3.75 9.93047 3.62 10.2405L0 8.69047L4.05 4.64047C4.52 4.17047 5.2 3.96047 5.86 4.09047L7.19 4.35047ZM9.17 15.0005C9.17 15.0005 12.91 13.4505 15.06 11.3005C20.46 5.90047 19.56 1.68047 19.27 0.730473C18.32 0.430473 14.1 -0.459527 8.7 4.94047C6.55 7.09047 5 10.8305 5 10.8305L9.17 15.0005ZM15.65 12.8105C13.36 14.8505 10.07 16.2505 9.76 16.3805L11.31 20.0005L15.36 15.9505C15.83 15.4805 16.04 14.8005 15.91 14.1405L15.65 12.8105ZM7 16.0005C7 16.8305 6.66 17.5805 6.12 18.1205C4.94 19.3005 0 20.0005 0 20.0005C0 20.0005 0.7 15.0605 1.88 13.8805C2.2988 13.4595 2.83325 13.1724 3.4155 13.0558C3.99775 12.9392 4.60153 12.9982 5.15017 13.2253C5.69882 13.4525 6.16758 13.8376 6.49694 14.3317C6.8263 14.8258 7.0014 15.4067 7 16.0005ZM11 7.00047C11 5.90047 11.9 5.00047 13 5.00047C14.1 5.00047 15 5.90047 15 7.00047C15 8.10047 14.1 9.00047 13 9.00047C11.9 9.00047 11 8.10047 11 7.00047Z" fill="#00696D"></path>
</svg>
0
</div>
<div>
<svg role="img" width="20" height="19" viewBox="0 0 20 19" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="likes-title">
<title id="likes-title">Likes</title>
<path d="M10 18.35L8.55 17.03C3.4 12.36 0 9.27 0 5.5C0 2.41 2.42 0 5.5 0C7.24 0 8.91 0.81 10 2.08C11.09 0.81 12.76 0 14.5 0C17.58 0 20 2.41 20 5.5C20 9.27 16.6 12.36 11.45 17.03L10 18.35Z"></path>
</svg>
0
</div>
</section>
<p class="originally-published">Originally published in <a href="https://dev.to/eevajonnapanula/another-year-of-being-in-gender-minority-in-tech-5akp">Dev.to.</a></p>
<img class="cover-photo" src="https://images.ctfassets.net/mpqufjsy02zr/2xzv50hEY7GRNpJryKcwMq/d5503269e590aab177e6e2394a73b3d8/wecoded-2024__1_.png" alt="Another Year Of Being in Gender Minority in Tech" />
<div class="blog-tags">
<a href="https://eevis.codes/tags/IWD"><span class="blog-tag purple">IWD</span></a>
</div>
<nav aria-label="Another Year Of Being in Gender Minority in Tech Table of Contents" class="table-of-contents">
<h2>Table of Contents</h2>
<ol>
<li><a href="https://eevis.codes/blog/2024-03-01/another-year-of-being-in-gender-minority-in-tech/#sometimes-it-just-sucks"> Sometimes it Just Sucks</a></li><li><a href="https://eevis.codes/blog/2024-03-01/another-year-of-being-in-gender-minority-in-tech/#you-can-be-an-ally"> You Can Be an Ally</a></li><li><a href="https://eevis.codes/blog/2024-03-01/another-year-of-being-in-gender-minority-in-tech/#wrapping-up"> Wrapping Up</a></li>
</ol>
</nav>
<p>It's time for Dev's annual We Coded campaign again, and it's the fifth time I'm participating. You can find my previous contributions from the links above. This year, I want to share experiences from another year as someone in gender minority in tech. I'll share some not-so-pleasant experiences and, after that, some moments of allyship. </p>
<p>Note that the stories I'm telling are from encounters with (assumed) men - and I'm referring to them generally as men here. I chose this approach because this celebration is about gender minorities in tech, and men belong to the majority. So, I'm not trying to paint a picture of men being something awful; it's more about the minority-majority aspect here. </p>
<p>Also, a content warning: I'm sharing some comments containing misogyny.</p>
<h2 id="sometimes-it-just-sucks">Sometimes it Just Sucks</h2>
<p>I've seen many men choose the path of not being a nice person. I'll share some stories from the past year alone.</p>
<p>There was this conversation in one Finnish tech-related Slack community where someone shared a piece of news about women not feeling welcome in tech. Multiple men were telling how this all is 90% in the heads of women, and how the way to solve the issues would be to stop using words like "tech bro" when describing, well, tech bros, and that it's not a problem because they haven't witnessed it.</p>
<p>When people who had witnessed and experienced the problems spoke up, they were either just ignored or told that they were "white knights," depending on the assumed gender. </p>
<p>Oh, and that conversation had my all-time favorite argument: "What about the x, y, and z fields? They don't have a proper gender balance." And that was said in a tech-centered Slack community, where the talk was about how women in tech feel excluded. I don't know if you noticed, but the topic was about tech, not the other fields. </p>
<p>I've also been told that women should respect men, and I don't belong even in a kitchen, but a cave, to learn about respect all the things men have done for me. This person made the threat in March last year, after I shared a LinkedIn post about gender equality and how some technologies are biased. Here's the whole comment:</p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7nsmmkph1xb9pmjkgs9a.png" alt="LinkedIn comment where Marko R. says some really mean things." /></p>
<p>There have been countless times that I've been speaking up about something related to tech, and my (man) colleague has needed to repeat what I've said to get the point through. And countless times when a man has been praised for something that I've done. </p>
<p>When talking about tech, I've been physically blocked from the conversation, as in I was standing in the circle of people and having a conversation, but when we started talking about the tech, one person turned their back on me, forcing me out of the circle. </p>
<p>And then there was this one incident, which paints a picture of our doubts: Is it me and something I did, or my gender? I was speaking at a conference and had just ended my talk. A person came to me and started giving their opinion about my talk. They used phrases like "I'm a speaker myself, and I teach speakers" and "You should have done the talk completely differently". </p>
<p>Let's pause here, I'll explain something. If you have ever spoken at an event, you know that the adrenaline rush speaking gives probably makes the minutes right after stepping down from the stage the worst time to give that kind of feedback. You will hardly remember anything the person said, just the feeling you had. </p>
<p>So, I thanked the person for their feedback (that was all I could do) and went somewhere quiet to calm down. I later spoke with several co-speakers at the conference, and they confirmed what I had thought: The feedback that person gave was not okay.</p>
<p>In those moments, it's tough to be a person from gender minority in tech. You never know if they would've given that same feedback to anyone in that situation - even if the speaker was a man - or if it was because the person felt somehow threatened by there being someone professional on the stage who is not from the majority.</p>
<p>The final example I wanted to share is from Dev. During the past year, I've shared several blog posts about gender equality, and last year's #WeCoded-post hit the jackpot. I mean, I knew that there would be at least some hateful comments, but the content surprised me. It's been getting worse year after year.</p>
<p>The comments were moderated pretty fast, but they contained things like drawing a line between asking for equality to mass murders and about wishing for "simpler times" - so, the times when it was women's job to just take care of the children and home, and be of service to men. And definitely not be a professional in any field. Oh, and then there was this one comment about enforcing equality being fascist. </p>
<p>These are just some examples from the past year. I've faced a lot more, but this already paints a picture that tech is not yet equal for all genders. We need to do better. </p>
<h2 id="you-can-be-an-ally">You Can Be an Ally</h2>
<p>Not all I've seen has been bad, and I'm thankful for that. I want to share some examples of allyship regarding gender equality in tech, too. </p>
<p>First, remember that part where I told you I needed a man colleague to repeat what I had said for the thing to be heard? That same colleague did that tirelessly and kept acknowledging the problem (that they needed to repeat what I said) and speaking up about it. So, they weren't trying to take credit for my ideas, but they amplified them, gave credit to me, and tried to solve the problem.</p>
<p>Then there were the other speakers I mentioned in the example about the feedback at the conference. When I got myself back together and could talk about the experience without starting to cry (yes, it felt that bad), the other speakers were understanding and on my side. They acknowledged the problem and assured me I didn't imagine the problem there. So, if you recognize yourself and remember being there and one of those people, thank you!</p>
<p> There's also one person close to me, without whom I wouldn't have survived through the last years. We've had so many conversations about gender equality in tech and all the things I've seen and endured, and that person has always listened to me, acknowledged the inequalities, and never questioned my feelings. They've also let me just rant and haven't tried to solve the problem in that situation. Everyone should have a person like that. </p>
<h2 id="wrapping-up">Wrapping Up</h2>
<p>In this blog post, I've shared my experiences from the past year as someone in gender minority in tech. There have been a lot of nasty things, but good things as well. Without the goods, I would have already left tech - something I've been thinking more and more about over the last year. </p>
<p>If you're in gender minority in tech, how has the last year been for you? And if you aren't, what are you doing for more equal tech? Share your thoughts in the comments!</p>
</article>
Why Your App Should Have Both Dark And Light Themes2024-03-07T00:00:00.000Zhttps://eevis.codes/blog/2024-03-07/why-your-app-should-have-both-dark-and-light-themes/
<nav id="breadcrumbs" aria-label="breadcrumbs">
<ol>
<li>
<a href="https://eevis.codes/blog/">Blog</a>
<span aria-hidden="true" class="breadcrumb-separator">/<span>
</span></span></li>
<li>Why Your App Should Have Both Dark And Light Themes</li><li>
</li></ol>
</nav>
<article class="blogPostLong">
<h1 id="/2024-03-07/why-your-app-should-have-both-dark-and-light-themes">Why Your App Should Have Both Dark And Light Themes</h1>
<div class="reading-time-and-date">
<p>
Reading time: about 3 minutes
</p>
<span class="separator"></span>
<p>Published <time datetime="7th Mar, 2024">7th Mar, 2024</time></p>
</div>
<section class="likes-and-boosts">
<div>
<svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="boosts-title">
<title id="boosts-title">Boosts</title>
<path d="M7.19 4.35047C5.15 6.64047 3.75 9.93047 3.62 10.2405L0 8.69047L4.05 4.64047C4.52 4.17047 5.2 3.96047 5.86 4.09047L7.19 4.35047ZM9.17 15.0005C9.17 15.0005 12.91 13.4505 15.06 11.3005C20.46 5.90047 19.56 1.68047 19.27 0.730473C18.32 0.430473 14.1 -0.459527 8.7 4.94047C6.55 7.09047 5 10.8305 5 10.8305L9.17 15.0005ZM15.65 12.8105C13.36 14.8505 10.07 16.2505 9.76 16.3805L11.31 20.0005L15.36 15.9505C15.83 15.4805 16.04 14.8005 15.91 14.1405L15.65 12.8105ZM7 16.0005C7 16.8305 6.66 17.5805 6.12 18.1205C4.94 19.3005 0 20.0005 0 20.0005C0 20.0005 0.7 15.0605 1.88 13.8805C2.2988 13.4595 2.83325 13.1724 3.4155 13.0558C3.99775 12.9392 4.60153 12.9982 5.15017 13.2253C5.69882 13.4525 6.16758 13.8376 6.49694 14.3317C6.8263 14.8258 7.0014 15.4067 7 16.0005ZM11 7.00047C11 5.90047 11.9 5.00047 13 5.00047C14.1 5.00047 15 5.90047 15 7.00047C15 8.10047 14.1 9.00047 13 9.00047C11.9 9.00047 11 8.10047 11 7.00047Z" fill="#00696D"></path>
</svg>
1
</div>
<div>
<svg role="img" width="20" height="19" viewBox="0 0 20 19" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="likes-title">
<title id="likes-title">Likes</title>
<path d="M10 18.35L8.55 17.03C3.4 12.36 0 9.27 0 5.5C0 2.41 2.42 0 5.5 0C7.24 0 8.91 0.81 10 2.08C11.09 0.81 12.76 0 14.5 0C17.58 0 20 2.41 20 5.5C20 9.27 16.6 12.36 11.45 17.03L10 18.35Z"></path>
</svg>
1
</div>
</section>
<img class="cover-photo" src="https://images.ctfassets.net/mpqufjsy02zr/51VAEArPG6vZ6RFqXt9uPQ/fbc001ecbedf9ab8ea6da4bc8cd0a157/light-and-dark__1_.png" alt="Your App Should Have Both Dark And Light Themes" />
<div class="blog-tags">
<a href="https://eevis.codes/tags/a11y"><span class="blog-tag turquoise">a11y</span></a> <a href="https://eevis.codes/tags/Android"><span class="blog-tag blue">Android</span></a>
</div>
<nav aria-label="Why Your App Should Have Both Dark And Light Themes Table of Contents" class="table-of-contents">
<h2>Table of Contents</h2>
<ol>
<li><a href="https://eevis.codes/blog/2024-03-07/why-your-app-should-have-both-dark-and-light-themes/#accessibility-of-dark-and-light-themes"> Accessibility of Dark and Light Themes</a></li><li><a href="https://eevis.codes/blog/2024-03-07/why-your-app-should-have-both-dark-and-light-themes/#technical-considerations"> Technical Considerations</a></li><li><a href="https://eevis.codes/blog/2024-03-07/why-your-app-should-have-both-dark-and-light-themes/#wrapping-up"> Wrapping Up</a></li><li><a href="https://eevis.codes/blog/2024-03-07/why-your-app-should-have-both-dark-and-light-themes/#links-in-the-blog-post"> Links in the Blog Post</a></li>
</ol>
</nav>
<p>You might have heard that your app should have a dark theme available. Some say it's because of preference, but it's actually also an accessibility thing.</p>
<p>This blog post itself doesn't have the code to enable dark and light themes for the user. Instead, it explains why having both of them is essential and provides further reading on the topic — yes, with links to some code and instructions as well. </p>
<h2 id="accessibility-of-dark-and-light-themes">Accessibility of Dark and Light Themes</h2>
<p>Many people use everything they can in a dark theme. After all, staring at screens for a long time might be easier if the user interface uses darker colors. Using a dark theme instead of a light one also saves some battery. </p>
<p>However, many people choose to use the dark theme because it is more accessible for them. For example, some vision issues might cause discomfort because of light or even wash out the rest of the person's vision. A white (or similar) background behind the text can cause these issues. Another reason is that dark themes often have better contrast (if they're designed properly). </p>
<p>Now, if the dark theme is so great, why not just have the dark theme for the app and forget about the light theme? While dark theme is more accessible for many, some people benefit from light theme. For example, people with astigmatism may find light theme more accessible, and people with dyslexia might prefer light mode for readability. For reference, see, for example, this article: <a href="https://uxdesign.cc/dark-matters-342ff2c7cc">Dark mode: How accessible design raises the bar - Layth Sihan</a></p>
<p>Luckily, defining light and dark themes in modern Android development is straightforward. Let's discuss the technical considerations of light and dark themes next. </p>
<h2 id="technical-considerations">Technical Considerations</h2>
<p>Starting from Android 10 (SDK 29), there is a system setting for switching between dark and light modes. If you define your themes correctly, your app will respect that system-wide setting by default. </p>
<p>To define a dark theme for your app using themes from XML files, follow the instructions in Android documentation: <a href="https://developer.android.com/develop/ui/views/theming/darktheme">Implement dark theme</a>. For apps using Jetpack Compose, the instructions are in <a href="https://developer.android.com/jetpack/compose/designsystems/material3#color-scheme">Material Design 3 in Compose - Color scheme</a>. </p>
<p>A handy tool for generating themes is <a href="https://m3.material.io/theme-builder">Material Theme Builder</a>, a web tool for defining themes. You can use the web UI to define the colors and then export the theme and the color definitions to import to your project. It provides exports for both Compose and XML, as well as for Flutter, web, and Material Tokens. </p>
<p>If you want to give the user more granular control and select the theme for your app different from the system setting, here's an article with a tutorial on how to do that with Jetpack Compose: <a href="https://medium.com/@sibel.nalop/dark-theme-switch-in-jetpack-compose-with-compositionlocal-f03bd54683bb">Dark Theme Switch in Jetpack Compose with CompositionLocal - Sibel Nalop</a></p>
<p>I mentioned that dark themes might have better contrast if they're designed properly. So, when defining your theme colors for both light and dark themes, check for enough contrast between the background, texts, and other UI elements. The Material Design docs explain color contrast: <a href="https://m3.material.io/foundations/accessible-design/patterns">Material Design 3: Accessible design—Color & Contrast</a>.</p>
<h2 id="wrapping-up">Wrapping Up</h2>
<p>In this blog post, I've discussed the accessibility of dark and light themes. As neither is the most accessible choice for all users, you should design and develop your apps to have both. They should either respect the user's system settings or have an in-app setting for switching between the themes.</p>
<p>Which do you personally prefer, the light or the dark theme? Do you select them app-by-app (if possible) or use the system-wide setting? And do you implement both for the apps you develop? </p>
<h2 id="links-in-the-blog-post">Links in the Blog Post</h2>
<ul>
<li><a href="https://uxdesign.cc/dark-matters-342ff2c7cc">Dark mode: How accessible design raises the bar - Layth Sihan</a></li>
<li><a href="https://developer.android.com/develop/ui/views/theming/darktheme">Implement dark theme</a></li>
<li><a href="https://developer.android.com/jetpack/compose/designsystems/material3#color-scheme">Material Design 3 in Compose - Color scheme</a></li>
<li><a href="https://medium.com/@sibel.nalop/dark-theme-switch-in-jetpack-compose-with-compositionlocal-f03bd54683bb">Dark Theme Switch in Jetpack Compose with CompositionLocal - Sibel Nalop</a></li>
<li><a href="https://m3.material.io/foundations/accessible-design/patterns">Material Design 3: Accessible design - Color & Contrast</a></li>
</ul>
<section>
<h2>Comments</h2>
<article class="comment">
<div class="author-image">
<img src="https://webmention.io/avatar/media.mas.to/7c027f79c3a38ec1d9020f506e565f4aa13215ba2d52602c1a5a4cdf9102255f.jpg" alt="Chris G" />
</div>
<div class="comment-content">
<div><a href="https://androiddev.social/@pula">Chris G</a>
<span class="separator"></span>
<time datetime="14th Mar, 2024">14th Mar, 2024</time>
</div>
<div class="comment">
<p>hey <span><a href="https://mas.to/@eevis">@<span>eevis</span></a></span>! your android accessibility checklist has been instrumental in my push at {dayjob} to expand our a11y, so I just wanted to say thank you so much!</p><p>not sure if you've seen Android Studio's compose UI check but you might want to consider adding it under the automated tools section of your checklist</p><p><a href="https://developer.android.com/studio/preview/features#compose-ui-check"><span>https://</span><span>developer.android.com/studio/p</span><span>review/features#compose-ui-check</span></a></p>
<a href="https://developer.android.com/studio/preview/features">New features in Android Studio Preview | Android Developers</a>
</div>
</div>
</article>
</section>
</article>
I'm Still Not One of the Guys2024-03-08T00:00:00.000Zhttps://eevis.codes/blog/2024-03-08/im-still-not-one-of-the-guys/
<nav id="breadcrumbs" aria-label="breadcrumbs">
<ol>
<li>
<a href="https://eevis.codes/blog/">Blog</a>
<span aria-hidden="true" class="breadcrumb-separator">/<span>
</span></span></li>
<li>I'm Still Not One of the Guys</li><li>
</li></ol>
</nav>
<article class="blogPostLong">
<h1 id="/2024-03-08/im-still-not-one-of-the-guys">I'm Still Not One of the Guys</h1>
<div class="reading-time-and-date">
<p>
Reading time: about 4 minutes
</p>
<span class="separator"></span>
<p>Published <time datetime="8th Mar, 2024">8th Mar, 2024</time></p>
</div>
<section class="likes-and-boosts">
<div>
<svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="boosts-title">
<title id="boosts-title">Boosts</title>
<path d="M7.19 4.35047C5.15 6.64047 3.75 9.93047 3.62 10.2405L0 8.69047L4.05 4.64047C4.52 4.17047 5.2 3.96047 5.86 4.09047L7.19 4.35047ZM9.17 15.0005C9.17 15.0005 12.91 13.4505 15.06 11.3005C20.46 5.90047 19.56 1.68047 19.27 0.730473C18.32 0.430473 14.1 -0.459527 8.7 4.94047C6.55 7.09047 5 10.8305 5 10.8305L9.17 15.0005ZM15.65 12.8105C13.36 14.8505 10.07 16.2505 9.76 16.3805L11.31 20.0005L15.36 15.9505C15.83 15.4805 16.04 14.8005 15.91 14.1405L15.65 12.8105ZM7 16.0005C7 16.8305 6.66 17.5805 6.12 18.1205C4.94 19.3005 0 20.0005 0 20.0005C0 20.0005 0.7 15.0605 1.88 13.8805C2.2988 13.4595 2.83325 13.1724 3.4155 13.0558C3.99775 12.9392 4.60153 12.9982 5.15017 13.2253C5.69882 13.4525 6.16758 13.8376 6.49694 14.3317C6.8263 14.8258 7.0014 15.4067 7 16.0005ZM11 7.00047C11 5.90047 11.9 5.00047 13 5.00047C14.1 5.00047 15 5.90047 15 7.00047C15 8.10047 14.1 9.00047 13 9.00047C11.9 9.00047 11 8.10047 11 7.00047Z" fill="#00696D"></path>
</svg>
0
</div>
<div>
<svg role="img" width="20" height="19" viewBox="0 0 20 19" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="likes-title">
<title id="likes-title">Likes</title>
<path d="M10 18.35L8.55 17.03C3.4 12.36 0 9.27 0 5.5C0 2.41 2.42 0 5.5 0C7.24 0 8.91 0.81 10 2.08C11.09 0.81 12.76 0 14.5 0C17.58 0 20 2.41 20 5.5C20 9.27 16.6 12.36 11.45 17.03L10 18.35Z"></path>
</svg>
0
</div>
</section>
<p class="originally-published">Originally published in <a href="https://dev.to/eevajonnapanula/im-still-not-one-of-the-guys-19e1">Dev.to.</a></p>
<img class="cover-photo" src="https://images.ctfassets.net/mpqufjsy02zr/735eX3Y4sEoMhy8L6EP7Pf/0535850cf274b2c03085a5471da9e3d6/wecoded-2024-2__1_.png" alt="I'm Still Not One of the Guys - #WeCoded" />
<div class="blog-tags">
<a href="https://eevis.codes/tags/IWD"><span class="blog-tag purple">IWD</span></a>
</div>
<nav aria-label="I'm Still Not One of the Guys Table of Contents" class="table-of-contents">
<h2>Table of Contents</h2>
<ol>
<li><a href="https://eevis.codes/blog/2024-03-08/im-still-not-one-of-the-guys/#its-about-language"> It's About Language</a></li><li><a href="https://eevis.codes/blog/2024-03-08/im-still-not-one-of-the-guys/#ive-had-to-fight-to-get-here-a-personal-perspective"> I've Had to Fight to Get Here - a Personal Perspective</a></li><li><a href="https://eevis.codes/blog/2024-03-08/im-still-not-one-of-the-guys/#if-its-exclusive-for-someone-there-are-other-words-to-use"> If It's Exclusive for Someone, There are Other Words to Use</a></li><li><a href="https://eevis.codes/blog/2024-03-08/im-still-not-one-of-the-guys/#wrapping-up"> Wrapping Up</a></li><li><a href="https://eevis.codes/blog/2024-03-08/im-still-not-one-of-the-guys/#links-in-the-blog-post"> Links in the Blog Post</a></li>
</ol>
</nav>
<p><em>As #WeCoded is a month-long this year, I'll post a couple of blog posts during this month. This post is the second of these posts.</em></p>
<p>In 2021, I wrote a blog post called <a href="https://eevis.codes/blog/2021-07-24/why-im-not-one-of-the-guys/">"Why I'm Not One of the Guys"</a> that got a response I didn't expect. When I published it in Dev, it received many comments about how I was wrong to say that the word "guys" is not gender-neutral. I still disagree, but to debate that is not the point of this blog post.</p>
<p>Since then, I've witnessed so many conversations where someone who does not identify as being part of "guys" asked not to be referred to by that word and got a harsh response. They're usually told how wrong they are and that they should just suck it up. </p>
<p>There are a couple of sides to this conversation. People are very protective of the language they're using - and at the same time, the language we use shapes the reality around us. And then there's the thing I keep wondering: if someone says they feel excluded, why is the reaction to tell them that they're wrong and their feelings are not valid? </p>
<h2 id="its-about-language">It's About Language</h2>
<p>The words we use matter to us. I've had and followed so many conversations where people try to justify the usage of racist, ableist, sexist, homo-, and transphobic words just because they've been using those words in the past. And this has happened even with people I know who are all in for equality. </p>
<p>And it kind of makes sense - language is a massive part of our identity. And if we use words that cause feelings of exclusion, and someone calls us out on that, it can feel like they're talking about us as persons, not our actions. </p>
<p>But even a well-meaning person can hurt. I often hear the phrase "Assume good intentions", and to some extent, I agree. It would be hard to communicate if others weren't assuming good intentions despite clumsy words. But there's a certain point where we should consider impact over intent. </p>
<p>Language holds a lot of power and can shape the reality around us. One great example is the Swedish gender-neutral pronoun "hen" (in addition to "hon"/she and "han"/he). It was (re)introduced to the Swedish vocabulary in 2012, and it has shaped how people think. (ref: <a href="https://www.pnas.org/doi/10.1073/pnas.1908156116">Tavits, M. & Pérez, E. O. - Language influences mass opinion toward gender and LGBT equality</a>)</p>
<h2 id="ive-had-to-fight-to-get-here---a-personal-perspective">I've Had to Fight to Get Here - a Personal Perspective</h2>
<p>You might wonder why this is so important to me. Why just not let it go? </p>
<p>It's personal. I'm someone from underrepresented genders in tech and who has faced a lot of biased opinions because of my gender. I've seen how big of a role it can play. </p>
<p>I've also tried to be one of the guys. I always felt out of place and that I didn't belong. And now you might mention something about men being different from each other - yes, that's true. But when it comes to "being one of the guys", it usually means just a really small subset of things. </p>
<p>So, I know I've been at a disadvantage throughout my career because of my gender and biases towards it. I've gone through a lot and don't want to hide who I am. I want to be recognized for the gender identity I have. And I'm not one of the guys.</p>
<h2 id="if-its-exclusive-for-someone-there-are-other-words-to-use">If It's Exclusive for Someone, There are Other Words to Use</h2>
<p>If someone comes to you and has the courage to say that the word you're using makes them feel excluded, why not just use another word? Good options are "folks," "all," "everyone," or "team," to name a few. </p>
<p>And I'm not saying that if there is, for example, a group of women (or other genders) who don't have any issues with the word "guys", you can't use it to describe them. The point is about people who feel excluded by using that word. </p>
<p>One last thing I want to mention is microaggressions. They're small actions that communicate some sort of bias towards a minority group. They're often unintentional (but sometimes intentional), like asking someone where they're actually from or calling only male employees to the meeting. If you're interested in reading more, I wrote a blog post <a href="https://eevis.codes/blog/2023-02-05/sometimes-i-feel-like-im-invisible-experiences-of-a-woman-in-tech/">"Sometimes I Feel Like I'm Invisible - Experiences of a Woman in Tech"</a> that explains microaggressions a bit more.</p>
<p>Microaggressions are something we, who are minorities in tech, face regularly. Personally, it's often been about actions that subtly suggest that men are better or more important - like being left out of a meeting or when my man-colleague is assumed to know better, even if I'm actually the expert on the topic. Or it's been about the language where men are the default, like with the word "guys". </p>
<h2 id="wrapping-up">Wrapping Up</h2>
<p>What am I trying to say with this blog post? The gist could be that if someone comes to you and tells you your word choices are exclusive, please listen to them. They're not there to criticize you as a person; it's about the words you use. </p>
<p>And for those of us who face these situations and feel excluded, we have the right to feel what we feel and to be treated in a way that we're not excluded. When we're asking for language that doesn't exclude us, we are right to do so. Let's not let anyone tell us otherwise. </p>
<h2 id="links-in-the-blog-post">Links in the Blog Post</h2>
<ul>
<li><a href="https://eevis.codes/blog/2021-07-24/why-im-not-one-of-the-guys/">"Why I'm Not One of the Guys"</a> </li>
<li><a href="https://www.pnas.org/doi/10.1073/pnas.1908156116">Tavits, M. & Pérez, E. O. - Language influences mass opinion toward gender and LGBT equality</a></li>
<li><a href="https://eevis.codes/blog/2023-02-05/sometimes-i-feel-like-im-invisible-experiences-of-a-woman-in-tech/">"Sometimes I Feel Like I'm Invisible - Experiences of a Woman in Tech"</a></li>
</ul>
</article>
Accessibility Checks with Jetpack Compose Previews2024-03-16T00:00:00.000Zhttps://eevis.codes/blog/2024-03-16/accessibility-checks-with-jetpack-compose-previews/
<nav id="breadcrumbs" aria-label="breadcrumbs">
<ol>
<li>
<a href="https://eevis.codes/blog/">Blog</a>
<span aria-hidden="true" class="breadcrumb-separator">/<span>
</span></span></li>
<li>Accessibility Checks with Jetpack Compose Previews</li><li>
</li></ol>
</nav>
<article class="blogPostLong">
<h1 id="/2024-03-16/accessibility-checks-with-jetpack-compose-previews">Accessibility Checks with Jetpack Compose Previews</h1>
<div class="reading-time-and-date">
<p>
Reading time: about 8 minutes
</p>
<span class="separator"></span>
<p>Published <time datetime="16th Mar, 2024">16th Mar, 2024</time></p>
</div>
<section class="likes-and-boosts">
<div>
<svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="boosts-title">
<title id="boosts-title">Boosts</title>
<path d="M7.19 4.35047C5.15 6.64047 3.75 9.93047 3.62 10.2405L0 8.69047L4.05 4.64047C4.52 4.17047 5.2 3.96047 5.86 4.09047L7.19 4.35047ZM9.17 15.0005C9.17 15.0005 12.91 13.4505 15.06 11.3005C20.46 5.90047 19.56 1.68047 19.27 0.730473C18.32 0.430473 14.1 -0.459527 8.7 4.94047C6.55 7.09047 5 10.8305 5 10.8305L9.17 15.0005ZM15.65 12.8105C13.36 14.8505 10.07 16.2505 9.76 16.3805L11.31 20.0005L15.36 15.9505C15.83 15.4805 16.04 14.8005 15.91 14.1405L15.65 12.8105ZM7 16.0005C7 16.8305 6.66 17.5805 6.12 18.1205C4.94 19.3005 0 20.0005 0 20.0005C0 20.0005 0.7 15.0605 1.88 13.8805C2.2988 13.4595 2.83325 13.1724 3.4155 13.0558C3.99775 12.9392 4.60153 12.9982 5.15017 13.2253C5.69882 13.4525 6.16758 13.8376 6.49694 14.3317C6.8263 14.8258 7.0014 15.4067 7 16.0005ZM11 7.00047C11 5.90047 11.9 5.00047 13 5.00047C14.1 5.00047 15 5.90047 15 7.00047C15 8.10047 14.1 9.00047 13 9.00047C11.9 9.00047 11 8.10047 11 7.00047Z" fill="#00696D"></path>
</svg>
6
</div>
<div>
<svg role="img" width="20" height="19" viewBox="0 0 20 19" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="likes-title">
<title id="likes-title">Likes</title>
<path d="M10 18.35L8.55 17.03C3.4 12.36 0 9.27 0 5.5C0 2.41 2.42 0 5.5 0C7.24 0 8.91 0.81 10 2.08C11.09 0.81 12.76 0 14.5 0C17.58 0 20 2.41 20 5.5C20 9.27 16.6 12.36 11.45 17.03L10 18.35Z"></path>
</svg>
5
</div>
</section>
<img class="cover-photo" src="https://images.ctfassets.net/mpqufjsy02zr/1XR2qwBUlmMYL3CGLBlDKq/dc28cfc521ce2cc71f71faceb0b9b2c7/previews-square.png" alt="Accessibility Checks with Jetpack Compose Previews" />
<div class="blog-tags">
<a href="https://eevis.codes/tags/a11y"><span class="blog-tag turquoise">a11y</span></a> <a href="https://eevis.codes/tags/Android"><span class="blog-tag blue">Android</span></a>
</div>
<nav aria-label="Accessibility Checks with Jetpack Compose Previews Table of Contents" class="table-of-contents">
<h2>Table of Contents</h2>
<ol>
<li><a href="https://eevis.codes/blog/2024-03-16/accessibility-checks-with-jetpack-compose-previews/#compose-ui-checks"> Compose UI Checks</a></li><li><a href="https://eevis.codes/blog/2024-03-16/accessibility-checks-with-jetpack-compose-previews/#accessibility-checks"> Accessibility Checks</a></li><li><a href="https://eevis.codes/blog/2024-03-16/accessibility-checks-with-jetpack-compose-previews/#examples"> Examples</a></li><li><a href="https://eevis.codes/blog/2024-03-16/accessibility-checks-with-jetpack-compose-previews/#wrapping-up"> Wrapping Up</a></li><li><a href="https://eevis.codes/blog/2024-03-16/accessibility-checks-with-jetpack-compose-previews/#links-in-the-blog-post"> Links in the Blog Post</a></li>
</ol>
</nav>
<p>Now that <a href="https://android-developers.googleblog.com/2024/02/android-studio-iguana-is-stable.html">Android Studio Iguana is out and stable</a>, I wanted to write about one feature it provides and I'm excited about: Accessibility checks with Compose previews. This feature is part of the new UI checks for Jetpack Compose, and in this blog post, I'll introduce you to how to use it and provide examples of the issues it finds.</p>
<h2 id="compose-ui-checks">Compose UI Checks</h2>
<p>UI Checks is a new feature shipped with Iguana. It provides a visual way of auditing your app with different screen sizes, font sizes, dark and light themes, and testing accessibility. It works similarly to how visual linting and accessibility checks integrations work for views. </p>
<p>Previews have this "Start UI Check Mode"-button at the top right corner:</p>
<p><img src="https://images.ctfassets.net/mpqufjsy02zr/BWDOewCIFj7C1FiXbq43f/463bab5efffc180992c8d5dfd571487f/Screenshot_2024-03-13_at_6.02.51.png" alt="Android Studio's Preview-UI. The leftmost icon on the top right corner of the preview is selected, and under it is an overlay with the text 'Start UI Check Mode'." /></p>
<p>After turning the feature on, it runs the checks and shows all the configurations with problems. If no issues are present, the preview UI doesn't show anything. Here is a picture of what the UI checks mode shows for me from one of the example checks I'll talk about in a bit:</p>
<p><img src="https://images.ctfassets.net/mpqufjsy02zr/9NUTE1ZrTRIusTSWfuyLj/5777d1d51251413e95ca799dae87b055/Screenshot_2024-03-13_at_6.10.02.png" alt="Overview of the UI Check results displaying different screen width previews, as well as different font sizes. In addition, one of the screens has a dark theme turned on." /></p>
<p>It's an overview picture on purpose, so the quality is not good enough to see the details if you try to zoom in. </p>
<p>You can find the problems the checker finds in the "Problems"-panel: </p>
<p><img src="https://images.ctfassets.net/mpqufjsy02zr/12JmSyrK8bk0F0fmI6hJao/83393f8b53aa0541b69cf7914c33b238/Screenshot_2024-03-13_at_6.23.34.png" alt="UI Check warnings from Android Studio. There is a warning 'Duplicate speakable text present', and it's selected. On the right side, there are more details about the problem this warning highlights." /></p>
<h2 id="accessibility-checks">Accessibility Checks</h2>
<p>The accessibility checks the UI checks mode runs are the same as, for example, Accessibility Scanner and Accessibility checks integration for views.</p>
<p>It checks content labeling, implementation, touch target size, and low contrast categories. You can find more information about each category and its contents in Android Accessibility Help's article <a href="https://support.google.com/accessibility/android/answer/6376559">Accessibility Scanner results</a>. The contents on that page are pretty much view-related, but each item in the categories has a "learn more" link, and many of them have examples with Compose as well. </p>
<p>One note, though: I was trying to reproduce some of the problems the checks should catch, and it didn't flag them. I think I've seen these false negatives (and some false positives) with the Accessibility Scanner app, which makes sense as they use the same API. </p>
<p>Let's dive deeper and discuss some example findings from the accessibility checks and how to fix them. </p>
<h2 id="examples">Examples</h2>
<h3 id="duplicate-speakable-text-present">Duplicate Speakable Text Present</h3>
<p>The first problem we will look at is "Duplicate Speakable Text Present". To demonstrate it, I've created a small component. It has two rows with a text and a button with text "Read more". The component is visible in the picture showing where you can turn on the UI Checks.</p>
<p>The description for the error is:</p>
<blockquote>
<p>This clickable item's speakable text: "Read more" is identical to that of 1 other item(s).</p>
</blockquote>
<p>Why is this a problem? Different assistive technologies and their users rely on content labels to navigate and understand the content. I'll give two examples of having two (or more!) duplicate clickable items and how they can be a problem.</p>
<p>First, a user who uses Voice Access for navigation would probably say "Tap Read More" when they want to activate that "Read more" button. But if there is more than one "Read more" button, Voice Access will ask which one. The user would need to pick the one by saying the number with which Voice Access has annotated it. Every time there is a similar situation, it slows the user down and decreases the user experience. Also, it means that they need to speak more to control the device, which might not be a problem when it's one time, but if that is repeated multiple times, it might become a problem. </p>
<p>Another example is screen reader users. They often navigate either linearly or use Touch to explore with TalkBack. If the user interface contains multiple elements with the same text label, distinguishing between the elements can be challenging. </p>
<p>So, how do we fix it? The answer is technically simple but might not be straightforward: The text in clickable elements must differ.</p>
<p>In this case, a solution could be to change the texts from "Read more" to "Read more about A" and "Read more about B". I mentioned that the solution may not be straightforward because it touches the copy, and we, as developers, don't always have control over it.</p>
<h3 id="no-speakable-text-present">No Speakable Text Present</h3>
<p>The next example I'm giving is an example of no speakable text present. The error description is:</p>
<blockquote>
<p>This item may not have a label readable by screen readers.</p>
</blockquote>
<p>One way to reproduce the problem and trigger this warning is to have an image or icon with an empty string in the content description:</p>
<pre><code class="language-kotlin"><span class="token function">Icon</span><span class="token punctuation">(</span>
<span class="token operator">..</span><span class="token punctuation">.</span>
contentDescrption<span class="token operator">=</span><span class="token string-literal singleline"><span class="token string">""</span></span>
<span class="token punctuation">)</span>
</code></pre><p>The problem is that if there is no speakable text present, then there is no text label for that element. Let's say this is an icon button with just this one icon element as a child. A screen reader user would hear that there is a button, but it doesn't have a label. They would not know what to do with that button because they wouldn't see the visual cues. </p>
<p>Fixing this depends on the icon - if it's purely decorative (for example, it accompanies an action to illustrate it), the content description can be <code>null</code>. But if it's the only thing, for instance, in a button (so, an icon button), it needs to have a descriptive label that communicates the action for the button.</p>
<p>If you're interested in reading more about content descriptions, I've written a blog post about content descriptions and how to write them: <a href="https://eevis.codes/blog/2023-11-15/how-to-add-content-descriptions-in-compose-a-guide-for-android-devs/">How to Add Content Descriptions in Compose - A Guide for Android Devs</a></p>
<h3 id="insufficient-color-contrast-ratio">Insufficient Color Contrast Ratio</h3>
<p>The third check is about color contrast. This check is actually twofold - image contrast and text contrast. The error description for the text-related issue is:</p>
<blockquote>
<p>The item's text contrast ratio is 4.16. This ratio is based on an estimated foreground color of #8E7098 and an estimated background color of #FFFBFF. Consider using colors that result in a contrast ratio greater than 4.50 for small text, or 3.00 for large text.</p>
</blockquote>
<p>So, I have a component with regular-sized text on a background, with colors that don't have enough contrast. </p>
<p>Why is this a problem? It's because of visibility. If there is not enough contrast between the background and the text or image, some users might not be able to read the text or see the image. This problem concerns people with different vision-related disabilities and people with cognitive disabilities, to name some groups. Furthermore, it can affect every single user. Have you ever tried using your phone in direct sunlight, and an app didn't have enough contrast to see the text?</p>
<p>Images should have a minimum contrast ratio of 3.0:1 between the image and the background. Text, on the other hand, should have a minimum of 3.0:1 for large text and 4.5:1 for small text. You can read more about color contrast and requirements from WebAIM's article <a href="https://webaim.org/articles/contrast/">Contrast and Color Accessibility</a></p>
<p>It's common to come across cases where there isn't enough contrast in the actual themes, especially when the app has both light and dark themes and has not been tested well enough. Some color combinations might work well with light themes, but their dark-theme counterparts don't have enough color contrast, or vice versa. </p>
<p>To fix the issue, the contrast between the background and texts or images must be increased. However, increasing the contrast may not be straightforward, as developers don't always have control over the color choices for the UI elements. </p>
<h3 id="touch-target">Touch Target</h3>
<p>The final check I'm writing about is for touch targets. The error description is:</p>
<blockquote>
<p>This item's height is 35dp. Consider making the height of this touch target 48dp or larger.</p>
</blockquote>
<p>In this case, I've created a button with a fixed height, which is why the touch target size is not high enough. The recommended touch target size minimum is 48dp x 48dp to ensure it's easier for anyone to press that touchable item. If, for example, a user has tremors in their hands, the bigger the touch target, the easier it is to tap it. </p>
<p>So, to fix the issue, you'll need to ensure that the touch target size is at least 48dp in width and 48 dp in height. Material Theme components should provide this out of the box - but if you're required to create custom implementations of buttons and other elements, remember to make sure they're at least 48dp x 48dp in size.</p>
<h2 id="wrapping-up">Wrapping Up</h2>
<p>In this blog post, we've looked at Compose UI Checks and how they can help with catching some accessibility issues in your app. While they are useful, it's important to remember that using them does not guarantee that your app is fully accessible. They might render some false positives or negatives, meaning that they either find issues where there are none or don't find issues that are present. And they catch only a small subset of accessibility issues, so more extensive testing is needed. </p>
<p>That being said, I'm happy about this new tool for finding accessibility issues during development without any additional tools like Accessibility Scanner (which is a great app!). It's usually more probable that the tools get used if they don't need extra effort. </p>
<p>Have you tested the UI Checks yet? How was it? Did you find something expected or unexpected?</p>
<h2 id="links-in-the-blog-post">Links in the Blog Post</h2>
<ul>
<li><a href="https://android-developers.googleblog.com/2024/02/android-studio-iguana-is-stable.html">Android Studio Iguana is out and stable</a></li>
<li><a href="https://support.google.com/accessibility/android/answer/6376559">Accessibility Scanner results</a></li>
<li><a href="https://eevis.codes/blog/2023-11-15/how-to-add-content-descriptions-in-compose-a-guide-for-android-devs/">How to Add Content Descriptions in Compose - A Guide for Android Devs</a></li>
<li><a href="https://webaim.org/articles/contrast/">Contrast and Color Accessibility</a></li>
</ul>
</article>