A place to cache linked articles (think custom and personal wayback machine)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

index.html 182KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229
  1. <!doctype html><!-- This is a valid HTML5 document. -->
  2. <!-- Screen readers, SEO, extensions and so on. -->
  3. <html lang="fr">
  4. <!-- Has to be within the first 1024 bytes, hence before the `title` element
  5. See: https://www.w3.org/TR/2012/CR-html5-20121217/document-metadata.html#charset -->
  6. <meta charset="utf-8">
  7. <!-- Why no `X-UA-Compatible` meta: https://stackoverflow.com/a/6771584 -->
  8. <!-- The viewport meta is quite crowded and we are responsible for that.
  9. See: https://codepen.io/tigt/post/meta-viewport-for-2015 -->
  10. <meta name="viewport" content="width=device-width,initial-scale=1">
  11. <!-- Required to make a valid HTML5 document. -->
  12. <title>Designing better target sizes (archive) — David Larlet</title>
  13. <meta name="description" content="Publication mise en cache pour en conserver une trace.">
  14. <!-- That good ol' feed, subscribe :). -->
  15. <link rel="alternate" type="application/atom+xml" title="Feed" href="/david/log/">
  16. <!-- Generated from https://realfavicongenerator.net/ such a mess. -->
  17. <link rel="apple-touch-icon" sizes="180x180" href="/static/david/icons2/apple-touch-icon.png">
  18. <link rel="icon" type="image/png" sizes="32x32" href="/static/david/icons2/favicon-32x32.png">
  19. <link rel="icon" type="image/png" sizes="16x16" href="/static/david/icons2/favicon-16x16.png">
  20. <link rel="manifest" href="/static/david/icons2/site.webmanifest">
  21. <link rel="mask-icon" href="/static/david/icons2/safari-pinned-tab.svg" color="#07486c">
  22. <link rel="shortcut icon" href="/static/david/icons2/favicon.ico">
  23. <meta name="msapplication-TileColor" content="#f7f7f7">
  24. <meta name="msapplication-config" content="/static/david/icons2/browserconfig.xml">
  25. <meta name="theme-color" content="#f7f7f7" media="(prefers-color-scheme: light)">
  26. <meta name="theme-color" content="#272727" media="(prefers-color-scheme: dark)">
  27. <!-- Is that even respected? Retrospectively? What a shAItshow…
  28. https://neil-clarke.com/block-the-bots-that-feed-ai-models-by-scraping-your-website/ -->
  29. <meta name="robots" content="noai, noimageai">
  30. <!-- Documented, feel free to shoot an email. -->
  31. <link rel="stylesheet" href="/static/david/css/style_2021-01-20.css">
  32. <!-- See https://www.zachleat.com/web/comprehensive-webfonts/ for the trade-off. -->
  33. <link rel="preload" href="/static/david/css/fonts/triplicate_t4_poly_regular.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)" crossorigin>
  34. <link rel="preload" href="/static/david/css/fonts/triplicate_t4_poly_bold.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)" crossorigin>
  35. <link rel="preload" href="/static/david/css/fonts/triplicate_t4_poly_italic.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)" crossorigin>
  36. <link rel="preload" href="/static/david/css/fonts/triplicate_t3_regular.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
  37. <link rel="preload" href="/static/david/css/fonts/triplicate_t3_bold.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
  38. <link rel="preload" href="/static/david/css/fonts/triplicate_t3_italic.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
  39. <script>
  40. function toggleTheme(themeName) {
  41. document.documentElement.classList.toggle(
  42. 'forced-dark',
  43. themeName === 'dark'
  44. )
  45. document.documentElement.classList.toggle(
  46. 'forced-light',
  47. themeName === 'light'
  48. )
  49. }
  50. const selectedTheme = localStorage.getItem('theme')
  51. if (selectedTheme !== 'undefined') {
  52. toggleTheme(selectedTheme)
  53. }
  54. </script>
  55. <meta name="robots" content="noindex, nofollow">
  56. <meta content="origin-when-cross-origin" name="referrer">
  57. <!-- Canonical URL for SEO purposes -->
  58. <link rel="canonical" href="https://ishadeed.com/article/target-size">
  59. <body class="remarkdown h1-underline h2-underline h3-underline em-underscore hr-center ul-star pre-tick" data-instant-intensity="viewport-all">
  60. <article>
  61. <header>
  62. <h1>Designing better target sizes</h1>
  63. </header>
  64. <nav>
  65. <p class="center">
  66. <a href="/david/" title="Aller à l’accueil"><svg class="icon icon-home">
  67. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-home"></use>
  68. </svg> Accueil</a> •
  69. <a href="https://ishadeed.com/article/target-size" title="Lien vers le contenu original">Source originale</a>
  70. <br>
  71. Mis en cache le 2024-01-10
  72. </p>
  73. </nav>
  74. <hr>
  75. <h2 id="intro">Intro</h2>
  76. <p>As a user, you need to interact with clickable UI elements like buttons, links, cards, and more.</p>
  77. <p>If an action has a small target size, it will be harder for the user to click, or they might click an adjacent action element by mistake.</p>
  78. <p>Let’s take a look at the following example.</p>
  79. <p><astro-island uid="1rP0JF" prefix="r1" component-url="https://ishadeed.com/_astro/Demo1.8eWum0Xi.js" component-export="default" renderer-url="https://ishadeed.com/_astro/client.2wOxL1Aq.js" props='{"helper":[0,true]}' ssr="" client="visible" opts='{"name":"Demo1","value":true}' await-children=""><div><p>Try to hover on an item ☝️ </p></div></astro-island></p>
  80. <p>Notice the small target size? We can address this by increasing the target size.</p>
  81. <p>When the target size is increased, it becomes easier for a user to click and interact with an element.</p>
  82. <p><astro-island uid="3MEby" prefix="r2" component-url="https://ishadeed.com/_astro/Demo1.8eWum0Xi.js" component-export="default" renderer-url="https://ishadeed.com/_astro/client.2wOxL1Aq.js" props='{"enhanced":[0,true]}' ssr="" client="visible" opts='{"name":"Demo1","value":true}' await-children=""></astro-island></p>
  83. <p><strong>Hover over the menu and toggle the checkbox</strong> to observe the difference between small and large target sizes.</p>
  84. <h2 id="what-is-a-target-size">What is a target size?</h2>
  85. <p><astro-island uid="2131rO" prefix="r71" component-url="https://ishadeed.com/_astro/Quote.7SNL3D4W.js" component-export="default" renderer-url="https://ishadeed.com/_astro/client.2wOxL1Aq.js" props='{"content":[0,"The specific region within a user interface element where a user clicks, touches, or taps to trigger an action."]}' ssr="" client="visible" opts='{"name":"Quote","value":true}' await-children=""><blockquote class="_quote_1a1hc_1"><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewbox="0 0 32 33"><g><rect y="0.527" fill="#D9BAF7" rx="16"></rect><g stroke="#7D45A7" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"><path d="M16.25 9.527c-3.5 0-5.25 2.75-5.25 5.25 0 4 3 4.5 3 6v2.25a1 1 0 001 1h2.5a1 1 0 001-1v-2.25c0-1.5 3-2 3-6 0-2.5-1.75-5.25-5.25-5.25z"></path><path d="M14.25 21.527h4"></path></g></g></svg><div><p>The specific region within a user interface element where a user clicks, touches, or taps to trigger an action.</p></div></blockquote></astro-island></p>
  86. <p>Today, users interact with a UI in various ways; it’s not solely about clicking an element with a mouse.</p>
  87. <p>Below is a table illustrating how multiple input types can function on the same device.</p>
  88. <p><picture> <source srcset="https://ishadeed.com/_astro/input-types.TICJ3qfC_26stTG.png" type="image/png"><source srcset="https://ishadeed.com/_astro/input-types.TICJ3qfC_ZHx551.webp" type="image/webp"> <img src="https://ishadeed.com/_astro/input-types.TICJ3qfC_26stTG.png" alt="A visual that shows the different input types for mobile, tablet, and desktop" loading="lazy" decoding="async"> </source></source></picture></p>
  89. <p>That thing caused to have many names for the topic:</p>
  90. <ul>
  91. <li>Apple calls it a <strong>hit target</strong></li>
  92. <li>Google Material calls it a <strong>Touch target</strong></li>
  93. <li>WAI call it a <strong>target size</strong></li>
  94. <li>Google LightSpeed test call it a <strong>Tap target</strong></li>
  95. <li><strong>Clickable area</strong>: used in the design community</li>
  96. </ul>
  97. <p>When talking about this, it’s important to know the context. For example, <strong>on mobile it’s the touch or tap</strong>, but on <strong>a device with a mouse it’s the click target</strong>.</p>
  98. <p>Here is an example of that.</p>
  99. <p><astro-island uid="Z14dVCp" prefix="r3" component-url="https://ishadeed.com/_astro/DemoContext.riEHUkJo.js" component-export="default" renderer-url="https://ishadeed.com/_astro/client.2wOxL1Aq.js" props='{"variation":[0,"2"]}' ssr="" client="visible" opts='{"name":"DemoContext","value":true}' await-children=""><div><div class="example-wrapper center"><div class="demo-context"><div><svg viewbox="0 0 28 29" fill="none" xmlns="http://www.w3.org/2000/svg"><g opacity="0.5"><path d="M12.3484 12.808C12.3484 14.675 15.1484 14.675 15.1484 12.808C15.1484 10.941 12.3484 10.941 12.3484 12.808Z" fill="black"></path><path d="M17.8914 12.808C17.8914 14.675 20.6914 14.675 20.6914 12.808C20.6914 10.941 17.8914 10.941 17.8914 12.808Z" fill="black"></path><path d="M6.94414 12.808C6.94414 14.675 9.74414 14.675 9.74414 12.808C9.74414 10.941 6.94414 10.941 6.94414 12.808Z" fill="black"></path><path d="M9.51938 3.20386H18.5079C23.6037 3.20386 27.7754 7.34814 27.7754 12.4724C27.7754 17.5967 23.6311 21.741 18.5068 21.741H16.1826L9.26688 26.8653L9.26797 21.7127C4.28369 21.5727 0.252249 17.4841 0.252249 12.4441C0.251156 7.34842 4.42369 3.20414 9.51969 3.20414L9.51938 3.20386ZM9.51938 20.4239H10.5836V24.2596L15.735 20.4239H18.5066C22.9023 20.4239 26.458 16.8681 26.458 12.4724C26.458 8.0767 22.9023 4.52098 18.5066 4.52098L9.51916 4.51988C5.12344 4.51988 1.56771 8.0756 1.56771 12.4713C1.56771 16.8682 5.12366 20.4239 9.51938 20.4239Z" fill="black"></path></g></svg><p>I checked the order page on my <span>phone</span> and tried to <span>tap</span> the order button but it’s not responding.</p></div></div></div></div></astro-island></p>
  100. <p>For clarity purposes, I will refer to the concept as target size for the rest of the guide, even though I wrote about it and called it <a href="https://ishadeed.com/article/clickable-area/">clickable area</a> years ago.</p>
  101. <h2 id="inclusive-target-size">Inclusive target size</h2>
  102. <h3 id="minimum-target-size">Minimum target size</h3>
  103. <p>According to the <a href="https://www.w3.org/WAI/WCAG22/Understanding/target-size-enhanced.html">WCAG 2.5.5</a> guidelines, the target size must at least be 24 by 24 CSS pixels. I want to emphasize that this is about the target size, not the icon size.</p>
  104. <p>Let’s take the following example. We have a mobile menu. The size of the icon is 24 by 24 pixels.</p>
  105. <p><picture> <source srcset="https://ishadeed.com/_astro/min-target-size-1.bf6twW3i_1qhhM0.png" type="image/png"><source srcset="https://ishadeed.com/_astro/min-target-size-1.bf6twW3i_ihIre.webp" type="image/webp"> <img src="https://ishadeed.com/_astro/min-target-size-1.bf6twW3i_1qhhM0.png" alt="" loading="lazy" decoding="async"> </source></source></picture></p>
  106. <p>However, the target size is 44 by 44 pixels. This is the area where the
  107. user can hover or tap.</p>
  108. <p>The recommended size differs from one platform to another. For example, Material for Android recommends a 48 by 48 pixels target size.</p>
  109. <p>As per the <a href="https://www.w3.org/WAI/WCAG22/Understanding/target-size-minimum.html">WCAG 2.5.8</a>, for a target size to pass, it should have a minimum of 24 by 24-pixel target size and it shouldn’t overlap with other targets.</p>
  110. <p><picture> <source srcset="https://ishadeed.com/_astro/min-target-size-2.bCoimqde_Z2staXt.png" type="image/png"><source srcset="https://ishadeed.com/_astro/min-target-size-2.bCoimqde_1tIouG.webp" type="image/webp"> <img src="https://ishadeed.com/_astro/min-target-size-2.bCoimqde_Z2staXt.png" alt="" loading="lazy" decoding="async"> </source></source></picture></p>
  111. <p>Notice when the target size is less than 24 by 24 pixels, the circles overlap.
  112. The target size failed. This is as per the WCAG 2.2 guidelines.</p>
  113. <figure><picture> <source srcset="https://ishadeed.com/_astro/min-target-size-3.6qqxqgwd_Z17Ni1N.png" type="image/png"><source srcset="https://ishadeed.com/_astro/min-target-size-3.6qqxqgwd_Z2fMQmz.webp" type="image/webp"> <img src="https://ishadeed.com/_astro/min-target-size-3.6qqxqgwd_Z17Ni1N.png" alt="" loading="lazy" decoding="async"> </source></source></picture> <figcaption><p>Left: the actions compared to the screen size. Right: the actions
  114. compared to my thumb finger.</p></figcaption></figure>
  115. <figure><picture> <source srcset="https://ishadeed.com/_astro/min-target-size-4.mBikg9wL_ZbVCKT.png" type="image/png"><source srcset="https://ishadeed.com/_astro/min-target-size-4.mBikg9wL_Z1jVc6F.webp" type="image/webp"> <img src="https://ishadeed.com/_astro/min-target-size-4.mBikg9wL_ZbVCKT.png" alt="" loading="lazy" decoding="async"> </source></source></picture> <figcaption><p>A side view of how my thumb covers 2-3 icons at a time.</p></figcaption></figure>
  116. <p>My recommendation is to have a target with a minimum size of 44 by 44 pixels, at least. This is per the WCAG 2.5.5.</p>
  117. <p>As per <a href="https://developers.google.com/cars/design/android-auto/design-system/sizing">Google Design for Driving</a> guidelines, the minimum touch target size is 76dp * 76dp.</p>
  118. <p>While driving a car, you have a very short time to tap something, so it should be large enough. This feels related to Fitt’s Law (below), where the size of a target should be larger if the time to tap is short.</p>
  119. <figure><picture> <source srcset="https://ishadeed.com/_astro/target-size-driving.4vvHpECm_1md4rh.png" type="image/png"><source srcset="https://ishadeed.com/_astro/target-size-driving.4vvHpECm_Z2ou8p8.webp" type="image/webp"> <img src="https://ishadeed.com/_astro/target-size-driving.4vvHpECm_1md4rh.png" alt="" loading="lazy" decoding="async"> </source></source></picture> <figcaption><p>Android Auto UI that shows the target sizes.
  120. <a href="https://www.figma.com/community/file/966820396083110735">Source</a></p></figcaption></figure>
  121. <p>It’s beneficial to have specific rules, but it doesn’t mean they should be followed blindly. It’s crucial to consistently test the UI targets.</p>
  122. <h3 id="applying-fitts-law">Applying Fitt’s Law</h3>
  123. <p><astro-island uid="175nIu" prefix="r72" component-url="https://ishadeed.com/_astro/Quote.7SNL3D4W.js" component-export="default" renderer-url="https://ishadeed.com/_astro/client.2wOxL1Aq.js" props="{&quot;content&quot;:[0,&quot;It's a principle that says it's faster to click or touch bigger objects that are closer to you.&quot;]}" ssr="" client="visible" opts='{"name":"Quote","value":true}' await-children=""><blockquote class="_quote_1a1hc_1"><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewbox="0 0 32 33"><g><rect y="0.527" fill="#D9BAF7" rx="16"></rect><g stroke="#7D45A7" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"><path d="M16.25 9.527c-3.5 0-5.25 2.75-5.25 5.25 0 4 3 4.5 3 6v2.25a1 1 0 001 1h2.5a1 1 0 001-1v-2.25c0-1.5 3-2 3-6 0-2.5-1.75-5.25-5.25-5.25z"></path><path d="M14.25 21.527h4"></path></g></g></svg><div><p>It's a principle that says it's faster to click or touch bigger objects that are closer to you.</p></div></blockquote></astro-island></p>
  124. <p>From a UI perspective, that means the bigger a call to action, the better for the user.</p>
  125. <p>In the following example, notice how much the distance between the input method and the button.</p>
  126. <p><picture> <source srcset="https://ishadeed.com/_astro/fitt-law-1.E_ddOyn4_JNlX3.png" type="image/png"><source srcset="https://ishadeed.com/_astro/fitt-law-1.E_ddOyn4_ZtLPYr.webp" type="image/webp"> <img src="https://ishadeed.com/_astro/fitt-law-1.E_ddOyn4_JNlX3.png" alt="" loading="lazy" decoding="async"> </source></source></picture></p>
  127. <p>With a larger button, the distance and time required for both the initial and final movements decrease.</p>
  128. <p><picture> <source srcset="https://ishadeed.com/_astro/fitt-law-2.JYstwono_Z2olxMJ.png" type="image/png"><source srcset="https://ishadeed.com/_astro/fitt-law-2.JYstwono_1rfn3H.webp" type="image/webp"> <img src="https://ishadeed.com/_astro/fitt-law-2.JYstwono_Z2olxMJ.png" alt="" loading="lazy" decoding="async"> </source></source></picture></p>
  129. <p>What are the initial and final movements? As per <a href="https://en.wikipedia.org/wiki/Fitts%27s_law">Wikipedia</a>:</p>
  130. <ul>
  131. <li><strong>Initial movement</strong>: a fast but imprecise movement towards the target.</li>
  132. <li><strong>Final movement</strong>: slower but more precise movement to tap or click the target.</li>
  133. </ul>
  134. <p>We can compare this to a real-life case. Imagine a physical keyboard. The
  135. buttons that need to be used the most (Enter, Esc, Space, Shift) are larger
  136. and closer to the user’s fingers than the rest of the buttons.</p>
  137. <p><picture> <source srcset="https://ishadeed.com/_astro/fitt-law-3.tRicewH2_rqW8J.png" type="image/png"><source srcset="https://ishadeed.com/_astro/fitt-law-3.tRicewH2_ZM9fNK.webp" type="image/webp"> <img src="https://ishadeed.com/_astro/fitt-law-3.tRicewH2_rqW8J.png" alt="" loading="lazy" decoding="async"> </source></source></picture></p>
  138. <p>Or even better, we can see that in action. In the previous section, I mentioned
  139. how the target sizes should be larger for car UIs, and how how that is
  140. somehow related to Fitt’s law.</p>
  141. <p><astro-island uid="1UNHK0" prefix="r4" component-url="https://ishadeed.com/_astro/CarUI.BOI0hj1P.js" component-export="default" renderer-url="https://ishadeed.com/_astro/client.2wOxL1Aq.js" props='{"helper":[0,"Toggle between the options and see how the target size changes."]}' ssr="" client="visible" opts='{"name":"CarUI","value":true}' await-children=""><div><p>Toggle between the options and see how the target size changes.</p></div></astro-island></p>
  142. <h3 id="spacing-around-the-targets">Spacing around the targets</h3>
  143. <p><astro-island uid="1pdmST" prefix="r73" component-url="https://ishadeed.com/_astro/Quote.7SNL3D4W.js" component-export="default" renderer-url="https://ishadeed.com/_astro/client.2wOxL1Aq.js" props='{"content":[0,"Make sure that the spacing between each target is enough and prevent clicking or tapping a target by mistake."]}' ssr="" client="visible" opts='{"name":"Quote","value":true}' await-children=""><blockquote class="_quote_1a1hc_1"><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewbox="0 0 32 33"><g><rect y="0.527" fill="#D9BAF7" rx="16"></rect><g stroke="#7D45A7" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"><path d="M16.25 9.527c-3.5 0-5.25 2.75-5.25 5.25 0 4 3 4.5 3 6v2.25a1 1 0 001 1h2.5a1 1 0 001-1v-2.25c0-1.5 3-2 3-6 0-2.5-1.75-5.25-5.25-5.25z"></path><path d="M14.25 21.527h4"></path></g></g></svg><div><p>Make sure that the spacing between each target is enough and prevent clicking or tapping a target by mistake.</p></div></blockquote></astro-island></p>
  144. <p>Consider the following example. We have three buttons, and the target size for each of them is 44 by 44 pixels.</p>
  145. <p><picture> <source srcset="https://ishadeed.com/_astro/target-spacing-1.I1X5nan6_1SRiX9.png" type="image/png"><source srcset="https://ishadeed.com/_astro/target-spacing-1.I1X5nan6_RBcdo.webp" type="image/webp"> <img src="https://ishadeed.com/_astro/target-spacing-1.I1X5nan6_1SRiX9.png" alt="" loading="lazy" decoding="async"> </source></source></picture></p>
  146. <p>When the user tries to tap an action on mobile, the average thumb size
  147. is large, thus might result in tapping an action item by mistake.</p>
  148. <p>In the following figure, the circle represents a thumb. Notice how it touches two target items.</p>
  149. <p><picture> <source srcset="https://ishadeed.com/_astro/target-spacing-2.kfqtbf8K_w16Iz.png" type="image/png"><source srcset="https://ishadeed.com/_astro/target-spacing-2.kfqtbf8K_Zuf01b.webp" type="image/webp"> <img src="https://ishadeed.com/_astro/target-spacing-2.kfqtbf8K_w16Iz.png" alt="" loading="lazy" decoding="async"> </source></source></picture></p>
  150. <p>According to a <a href="http://touchlab.mit.edu/publications/2003_009.pdf">study</a>
  151. by the MIT Touch Lab of human fingertips, the average size of the index
  152. finger is 1.6 to 2 cm, which translates to a width of 45 - 57 pixels.</p>
  153. <p>To apply that, we need to increase the spacing between the items.</p>
  154. <p><picture> <source srcset="https://ishadeed.com/_astro/target-spacing-3.7pTWY6zY_l5OPl.png" type="image/png"><source srcset="https://ishadeed.com/_astro/target-spacing-3.7pTWY6zY_ZFagTp.webp" type="image/webp"> <img src="https://ishadeed.com/_astro/target-spacing-3.7pTWY6zY_l5OPl.png" alt="" loading="lazy" decoding="async"> </source></source></picture></p>
  155. <p>Now it’s less likely that the user will tap a target by mistake.</p>
  156. <h3 id="visual-target-feedback">Visual target feedback</h3>
  157. <p><astro-island uid="2bhe3X" prefix="r74" component-url="https://ishadeed.com/_astro/Quote.7SNL3D4W.js" component-export="default" renderer-url="https://ishadeed.com/_astro/client.2wOxL1Aq.js" props='{"content":[0,"When hovering over or tapping a target, displaying visual feedback is beneficial, as it enhances the time to action."]}' ssr="" client="visible" opts='{"name":"Quote","value":true}' await-children=""><blockquote class="_quote_1a1hc_1"><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewbox="0 0 32 33"><g><rect y="0.527" fill="#D9BAF7" rx="16"></rect><g stroke="#7D45A7" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"><path d="M16.25 9.527c-3.5 0-5.25 2.75-5.25 5.25 0 4 3 4.5 3 6v2.25a1 1 0 001 1h2.5a1 1 0 001-1v-2.25c0-1.5 3-2 3-6 0-2.5-1.75-5.25-5.25-5.25z"></path><path d="M14.25 21.527h4"></path></g></g></svg><div><p>When hovering over or tapping a target, displaying visual feedback is beneficial, as it enhances the time to action.</p></div></blockquote></astro-island></p>
  158. <p>This is useful for elements without clear boundaries. For example, a link or an icon-only button.</p>
  159. <p>Try to hover over one of the buttons below.</p>
  160. <p><astro-island uid="1Q9JtP" prefix="r75" component-url="https://ishadeed.com/_astro/VisualFeedback.KoO0MgZL.js" component-export="default" renderer-url="https://ishadeed.com/_astro/client.2wOxL1Aq.js" props='{"variation":[0,"1"]}' ssr="" client="visible" opts='{"name":"VisualFeedback","value":true}' await-children=""></astro-island></p>
  161. <p>Adding visual feedback sets expectations for the user. It’s like a hint that lets them know the target area boundaries.</p>
  162. <p>Here is another example of a menu.</p>
  163. <p><astro-island uid="13Y4L0" prefix="r76" component-url="https://ishadeed.com/_astro/VisualFeedback.KoO0MgZL.js" component-export="default" renderer-url="https://ishadeed.com/_astro/client.2wOxL1Aq.js" props='{"variation":[0,"2"]}' ssr="" client="visible" opts='{"name":"VisualFeedback","value":true}' await-children=""></astro-island></p>
  164. <h3 id="multi-input-sources">Multi-input sources</h3>
  165. <p><astro-island uid="1l9apY" prefix="r77" component-url="https://ishadeed.com/_astro/Quote.7SNL3D4W.js" component-export="default" renderer-url="https://ishadeed.com/_astro/client.2wOxL1Aq.js" props='{"content":[0,"A target size that works well with a mouse might be hard to tap on mobile. Make sure the target size is big enough."]}' ssr="" client="visible" opts='{"name":"Quote","value":true}' await-children=""><blockquote class="_quote_1a1hc_1"><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewbox="0 0 32 33"><g><rect y="0.527" fill="#D9BAF7" rx="16"></rect><g stroke="#7D45A7" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"><path d="M16.25 9.527c-3.5 0-5.25 2.75-5.25 5.25 0 4 3 4.5 3 6v2.25a1 1 0 001 1h2.5a1 1 0 001-1v-2.25c0-1.5 3-2 3-6 0-2.5-1.75-5.25-5.25-5.25z"></path><path d="M14.25 21.527h4"></path></g></g></svg><div><p>A target size that works well with a mouse might be hard to tap on mobile. Make sure the target size is big enough.</p></div></blockquote></astro-island></p>
  166. <p>An example of that would be to compare how clicking a target with a mouse versus tapping with a finger.</p>
  167. <p><picture> <source srcset="https://ishadeed.com/_astro/target-rwd-1.amrPG9os_1IvNtk.png" type="image/png"><source srcset="https://ishadeed.com/_astro/target-rwd-1.amrPG9os_KBtxm.webp" type="image/webp"> <img src="https://ishadeed.com/_astro/target-rwd-1.amrPG9os_1IvNtk.png" alt="" loading="lazy" decoding="async"> </source></source></picture></p>
  168. <p>In this example, the radio button is small compared to the user’s finger,
  169. but it’s almost the same size as the computer pointer. The idea here is
  170. to make sure a target size works with multiple input sources.</p>
  171. <h2 id="what-to-consider">What to consider</h2>
  172. <h3 id="touch-size-differs-based-on-finger-size">Touch size differs based on finger size</h3>
  173. <p>There is a common term which is ”<strong>fat finger</strong> problem”. It means that the user’s finger is too big to accurately tap on the touch targers. In this case, I can blame the small touch targets only.</p>
  174. <p>To demonstrate that, I made the following interactive playground that helps in visualizing your finger touch size.</p>
  175. <p><strong>Please note that the following demo works on mobile only. Not on mobile, no problem! See the video tab.</strong></p>
  176. <p><astro-island uid="1Wgx9I" prefix="r5" component-url="https://ishadeed.com/_astro/TouchTest.EuL0qnw3.js" component-export="default" renderer-url="https://ishadeed.com/_astro/client.2wOxL1Aq.js" props="{}" ssr="" client="visible" opts='{"name":"TouchTest$1","value":true}' await-children=""><div><p class="_tabsWrapper_3fegj_69"><button class="_button_3fegj_74 _isActive_3fegj_87">Demo</button><button class="_button_3fegj_74">Video</button></p><div id="touchTest" class="example-wrapper center _touchTestwrapper_3fegj_1"><div><div><p>Try to tap any of the following menu items.</p><p class="_box_3fegj_62"></p></div><p>This is an estimation only. It will differ from a device to another.</p></div></div><p><video poster="https://ishadeed.com/assets/target-size/videos/touch-test.png" src="https://ishadeed.com/assets/target-size/videos/touch-test.mp4" controls="" controlslist="nodownload"></video></p></div></astro-island></p>
  177. <p>I used the snippet shown on the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Touch/radiusX">MDN website</a>. While this might not be perfect, at least it’s useful to demonstrate the concept.</p>
  178. <p>We need to get the <code>radiusX</code> and <code>radiusY</code>, then multiply them by 2.</p>
  179. <pre class="language-js"><code is:raw="" class="language-js">box<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">"touchstart"</span><span class="token punctuation">,</span> getTouchSize<span class="token punctuation">)</span>
  180. <span class="token keyword">function</span> <span class="token function">getTouchSize</span><span class="token punctuation">(</span><span class="token parameter">e</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  181. <span class="token keyword">const</span> touch <span class="token operator">=</span> e<span class="token punctuation">.</span>changedTouches<span class="token punctuation">.</span><span class="token function">item</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span>
  182. elm<span class="token punctuation">.</span>style<span class="token punctuation">.</span>width <span class="token operator">=</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>touch<span class="token punctuation">.</span>radiusX <span class="token operator">*</span> <span class="token number">2</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">px</span><span class="token template-punctuation string">`</span></span>
  183. elm<span class="token punctuation">.</span>style<span class="token punctuation">.</span>height <span class="token operator">=</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>touch<span class="token punctuation">.</span>radiusY <span class="token operator">*</span> <span class="token number">2</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">px</span><span class="token template-punctuation string">`</span></span>
  184. <span class="token punctuation">}</span></code></pre>
  185. <p>We can take this further and show what the target size looks like on your phone. Once you tap on the cross area, the UI will get your finger size and append it to the question.</p>
  186. <p>Notice how the target size is compared to the radio button.</p>
  187. <p><astro-island uid="Z1d9DfT" prefix="r6" component-url="https://ishadeed.com/_astro/TouchTestDemo.63BHA1EC.js" component-export="default" renderer-url="https://ishadeed.com/_astro/client.2wOxL1Aq.js" props="{}" ssr="" client="visible" opts='{"name":"TouchTest","value":true}' await-children=""><div><p class="_tabsWrapper_1yyl2_82"><button class="_button_1yyl2_87 _isActive_1yyl2_100">Demo</button><button class="_button_1yyl2_87">Video</button></p><div class="demo-content"><div><div id="touchTesDemo" class="example-wrapper center _touchTestwrapperDemo_1yyl2_1"><div><div class="_touchResult_1yyl2_21"><p>W: 32, H: 32</p><p class="_cross_1yyl2_33"></p><p>👋 Tap here to get your finger size</p></div></div></div></div><p><video poster="https://ishadeed.com/assets/target-size/videos/touch-demo.png" src="https://ishadeed.com/assets/target-size/videos/touch-demo.mp4" controls="" controlslist="nodownload"></video></p></div></div></astro-island></p>
  188. <p>Interesting, right?</p>
  189. <h3 id="touch-size-differs-based-on-the-touch-angle">Touch size differs based on the touch angle</h3>
  190. <p>It’s important to keep in mind that the user might use the touch in two different ways:</p>
  191. <ul>
  192. <li>Touch with fingertip</li>
  193. <li>Touch with the finger pad</li>
  194. </ul>
  195. <h4 id="fingertip">Fingertip</h4>
  196. <p>When using the fingertip, the finger is titled with an angle.</p>
  197. <h4 id="finger-pad">Finger pad</h4>
  198. <p>When using the finger pad, the finger isn’t tilted, but this can increase the issue of the target being hidden while the user’s finger is above it.</p>
  199. <p><picture> <source srcset="https://ishadeed.com/_astro/finger-tip-1.Dxt6CXvq_Z187QcV.png" type="image/png"><source srcset="https://ishadeed.com/_astro/finger-tip-1.Dxt6CXvq_Z262b8T.webp" type="image/webp"> <img src="https://ishadeed.com/_astro/finger-tip-1.Dxt6CXvq_Z187QcV.png" alt="" loading="lazy" decoding="async"> </source></source></picture></p>
  200. <p>In the following illustration, notice how the search icon is still visible
  201. when the user is using their fingertip.</p>
  202. <p><picture> <source srcset="https://ishadeed.com/_astro/finger-tip-2.mERclBS9_2hs6Rl.png" type="image/png"><source srcset="https://ishadeed.com/_astro/finger-tip-2.mERclBS9_1jxLVn.webp" type="image/webp"> <img src="https://ishadeed.com/_astro/finger-tip-2.mERclBS9_2hs6Rl.png" alt="" loading="lazy" decoding="async"> </source></source></picture>
  203. <astro-island uid="2fkQFq" prefix="r78" component-url="https://ishadeed.com/_astro/Quote.7SNL3D4W.js" component-export="default" renderer-url="https://ishadeed.com/_astro/client.2wOxL1Aq.js" props="{&quot;content&quot;:[0,&quot;Small touch targets make users work harder because they require more accuracy to hit. Users need to reorient their finger, from finger pad to fingertip, to hit the target with clear visual feedback. Using the finger pad would cover the entire target, making it impossible for users to see the target they're trying to hit.&quot;],&quot;source&quot;:[0,&quot;UX movement&quot;],&quot;link&quot;:[0,&quot;https://uxmovement.com/mobile/finger-friendly-design-ideal-mobile-touch-target-sizes/&quot;]}" ssr="" client="visible" opts='{"name":"Quote","value":true}' await-children=""><blockquote class="_quote_1a1hc_1"><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewbox="0 0 32 33"><g><rect y="0.527" fill="#D9BAF7" rx="16"></rect><g stroke="#7D45A7" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"><path d="M16.25 9.527c-3.5 0-5.25 2.75-5.25 5.25 0 4 3 4.5 3 6v2.25a1 1 0 001 1h2.5a1 1 0 001-1v-2.25c0-1.5 3-2 3-6 0-2.5-1.75-5.25-5.25-5.25z"></path><path d="M14.25 21.527h4"></path></g></g></svg><div><p>Small touch targets make users work harder because they require more accuracy to hit. Users need to reorient their finger, from finger pad to fingertip, to hit the target with clear visual feedback. Using the finger pad would cover the entire target, making it impossible for users to see the target they're trying to hit.</p><p>- <a href="https://uxmovement.com/mobile/finger-friendly-design-ideal-mobile-touch-target-sizes/">UX movement</a></p></div></blockquote></astro-island></p>
  204. <p>The touch with the finger pad is often at an angle, which makes it shape like an oval. The touch with the finger pad is larger but often hides the target you’re tapping.</p>
  205. <p>Here is a figure that shows a comparison.</p>
  206. <p><picture> <source srcset="https://ishadeed.com/_astro/finger-tip-3.xY66qdf2_Z12Ps9n.png" type="image/png"><source srcset="https://ishadeed.com/_astro/finger-tip-3.xY66qdf2_ZsKIbK.webp" type="image/webp"> <img src="https://ishadeed.com/_astro/finger-tip-3.xY66qdf2_Z12Ps9n.png" alt="" loading="lazy" decoding="async"> </source></source></picture></p>
  207. <h3 id="small-touch-sizes-are-hard-to-use-in-shaking-environments">Small touch sizes are hard to use in shaking environments</h3>
  208. <p>When a user is trying to tap a small target size, it isn’t that they are sitting in a perfect environment and just staring at the screen. People are busy and they want to achieve their goals fast.</p>
  209. <p>In the following interactive demo, I tried to simulate how a shaking environment makes using a small target much harder.</p>
  210. <ul>
  211. <li>Turn on the “Stimulate shaking” toggle.</li>
  212. <li>Try to select one of the icons.</li>
  213. </ul>
  214. <p><astro-island uid="ZHilQs" prefix="r7" component-url="https://ishadeed.com/_astro/ShakingPhone.9bY4X2KJ.js" component-export="default" renderer-url="https://ishadeed.com/_astro/client.2wOxL1Aq.js" props="{}" ssr="" client="visible" opts='{"name":"ShakingPhone$1","value":true}' await-children=""><div><div class="shaking-test"><p><strong>⚠️ Motion warning:</strong> this example may affect people with visual sensitivities.</p><div class="box"><div class="content"><h3>Try to tap on one of the navigation items</h3><p>This is how it feels to navigate small target areas with a shaking device. Not cool, right?</p></div><p class="touch"></p></div><div class="info"><p>A simulation of how a mobile can shake in situation like being in public transportation, or driving on bumpy roads.</p></div></div></div></astro-island></p>
  215. <p>It’s annoying, isn’t it? I hope the demo gave you the vibes of when you tap something by mistake.</p>
  216. <h3 id="using-touch-with-the-fingers-covered">Using touch with the fingers covered</h3>
  217. <p>A user might wear a glove in the winter and is trying to tab on a UI. This is a common and expected thing. In this case, if the target sizes are small and close to each other, the user will find a hard time trying to tap on an item.</p>
  218. <p>Here is a mocked comparison between the natural touch and the touch with gloves. Notice that it got slightly bigger with gloves. This is important to take into consideration.</p>
  219. <p><picture> <source srcset="https://ishadeed.com/_astro/fingers-covered-1.gzu_BN_n_2qOi6k.png" type="image/png"><source srcset="https://ishadeed.com/_astro/fingers-covered-1.gzu_BN_n_1iOIKy.webp" type="image/webp"> <img src="https://ishadeed.com/_astro/fingers-covered-1.gzu_BN_n_2qOi6k.png" alt="" loading="lazy" decoding="async"> </source></source></picture></p>
  220. <p><strong>In the following example</strong>, try to switch the context between “Natural
  221. touch” or “Touch with gloves” and notice how it becomes harder to select
  222. a target.</p>
  223. <p>This is similar to what a user with gloves on experience.</p>
  224. <p><astro-island uid="1OXYxj" prefix="r8" component-url="https://ishadeed.com/_astro/HandGloves.61JqWuZ9.js" component-export="default" renderer-url="https://ishadeed.com/_astro/client.2wOxL1Aq.js" props="{}" ssr="" client="visible" opts='{"name":"ShakingPhone","value":true}' await-children=""><div class="hand-gloves"><div class="box"><div class="content"><h3>Try to tap on one of the navigation items</h3><p>This is a demo that shows the size of your finger when wearing a glove.</p></div><p class="touch"></p></div><div class="info"><p>The touch size interferes with the two actions.</p></div></div></astro-island></p>
  225. <h3 id="spacing">Spacing</h3>
  226. <p>There are exceptions for a target size like inline elements or when there is a small target but it has a large spacing around it.</p>
  227. <p>According to the <a href="https://www.w3.org/WAI/WCAG22/Understanding/target-size-minimum.html#dfn-pointer-input">WCAG 2.58</a>:</p>
  228. <p><astro-island uid="Z1728rp" prefix="r79" component-url="https://ishadeed.com/_astro/Quote.7SNL3D4W.js" component-export="default" renderer-url="https://ishadeed.com/_astro/client.2wOxL1Aq.js" props='{"content":[0,"Undersized targets (those less than 24 by 24 CSS pixels) are positioned so that if a 24 CSS pixel diameter circle is centered on the bounding box of each, the circles do not intersect another target or the circle for another undersized target;"]}' ssr="" client="visible" opts='{"name":"Quote","value":true}' await-children=""><blockquote class="_quote_1a1hc_1"><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewbox="0 0 32 33"><g><rect y="0.527" fill="#D9BAF7" rx="16"></rect><g stroke="#7D45A7" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"><path d="M16.25 9.527c-3.5 0-5.25 2.75-5.25 5.25 0 4 3 4.5 3 6v2.25a1 1 0 001 1h2.5a1 1 0 001-1v-2.25c0-1.5 3-2 3-6 0-2.5-1.75-5.25-5.25-5.25z"></path><path d="M14.25 21.527h4"></path></g></g></svg><div><p>Undersized targets (those less than 24 by 24 CSS pixels) are positioned so that if a 24 CSS pixel diameter circle is centered on the bounding box of each, the circles do not intersect another target or the circle for another undersized target;</p></div></blockquote></astro-island></p>
  229. <h4 id="links">Links</h4>
  230. <p>For example, a link that is part of a paragraph is excluded from the target size recommendations.</p>
  231. <p>See the following example.</p>
  232. <p><astro-island uid="Z2heG9M" prefix="r80" component-url="https://ishadeed.com/_astro/Links.o41XTd_H.js" component-export="default" renderer-url="https://ishadeed.com/_astro/client.2wOxL1Aq.js" props='{"variation":[0,"1"]}' ssr="" client="visible" opts='{"name":"Links","value":true}' await-children=""><div class="_links_65166_1 example-wrapper center"><p>As per <a href="#">this article</a>, it's recommended to stay calm at all times.</p></div></astro-island></p>
  233. <p>The target size for the link is the same as the line height. No need to make it larger.</p>
  234. <p>However, that doesn’t mean we shouldn’t care about links. It’s equally important to a regular target area.</p>
  235. <p><astro-island uid="171Aws" prefix="r81" component-url="https://ishadeed.com/_astro/Links.o41XTd_H.js" component-export="default" renderer-url="https://ishadeed.com/_astro/client.2wOxL1Aq.js" props='{"variation":[0,"2"]}' ssr="" client="visible" opts='{"name":"Links","value":true}' await-children=""><div class="_links_65166_1 example-wrapper center"><div><p class="_desc_65166_5"><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewbox="0 0 17 17"><g clip-path="url(#clip0_2216_428)"><path fill="#E73C65" d="M8.5.928C4.31.928.9 4.336.9 8.528s3.409 7.6 7.6 7.6c4.192 0 7.6-3.408 7.6-7.6s-3.408-7.6-7.6-7.6zm2.512 9.103a.705.705 0 01-.496 1.2.664.664 0 01-.496-.208L8.5 9.52l-1.503 1.503a.692.692 0 01-.497.208.664.664 0 01-.496-.208.705.705 0 010-.992l1.504-1.503-1.504-1.504a.705.705 0 010-.992.705.705 0 01.992 0L8.5 7.536l1.503-1.504a.705.705 0 01.992 0 .705.705 0 010 .992L9.492 8.528l1.52 1.503z"></path></g><defs><clippath id="clip0_2216_428"><path fill="#fff" d="M0 0H16V16H0z" transform="translate(.5 .527)"></path></clippath></defs></svg>Don’t do this. The line height is too small.</p><p>As per this article, it’s recommended to keep calm at all times. If you need help, feel free to <a href="#">get in touch</a> for an appointment.</p></div></div></astro-island></p>
  236. <p>It can get worse if there are multiple links in the same paragraph:</p>
  237. <p><astro-island uid="4pPB7" prefix="r82" component-url="https://ishadeed.com/_astro/Links.o41XTd_H.js" component-export="default" renderer-url="https://ishadeed.com/_astro/client.2wOxL1Aq.js" props='{"variation":[0,"3"]}' ssr="" client="visible" opts='{"name":"Links","value":true}' await-children=""><div class="_links_65166_1 example-wrapper center"><div><p class="_desc_65166_5"><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewbox="0 0 17 17"><g clip-path="url(#clip0_2216_428)"><path fill="#E73C65" d="M8.5.928C4.31.928.9 4.336.9 8.528s3.409 7.6 7.6 7.6c4.192 0 7.6-3.408 7.6-7.6s-3.408-7.6-7.6-7.6zm2.512 9.103a.705.705 0 01-.496 1.2.664.664 0 01-.496-.208L8.5 9.52l-1.503 1.503a.692.692 0 01-.497.208.664.664 0 01-.496-.208.705.705 0 010-.992l1.504-1.503-1.504-1.504a.705.705 0 010-.992.705.705 0 01.992 0L8.5 7.536l1.503-1.504a.705.705 0 01.992 0 .705.705 0 010 .992L9.492 8.528l1.52 1.503z"></path></g><defs><clippath id="clip0_2216_428"><path fill="#fff" d="M0 0H16V16H0z" transform="translate(.5 .527)"></path></clippath></defs></svg>Don’t do this. The line height is too small.</p><p>As per this article, it’s recommended to <a href="#">keep calm</a> at all times. If you need help, feel free to <a href="#">get in touch</a> for an appointment.</p></div></div></astro-island></p>
  238. <p>Instead, we should increase the line height in CSS:</p>
  239. <pre class="language-css"><code is:raw="" class="language-css"><span class="token selector">p</span> <span class="token punctuation">{</span>
  240. <span class="token property">line-height</span><span class="token punctuation">:</span> 1.4<span class="token punctuation">;</span>
  241. <span class="token punctuation">}</span></code></pre>
  242. <p><astro-island uid="1DIU91" prefix="r83" component-url="https://ishadeed.com/_astro/Links.o41XTd_H.js" component-export="default" renderer-url="https://ishadeed.com/_astro/client.2wOxL1Aq.js" props='{"variation":[0,"4"]}' ssr="" client="visible" opts='{"name":"Links","value":true}' await-children=""><div class="_links_65166_1 example-wrapper center"><div><p>As per this article, it’s recommended to <a href="#">keep calm</a> at all times. If you need help, feel free to <a href="#">get in touch</a> for an appointment.</p></div></div></astro-island></p>
  243. <h4 id="more-spacing-for-small-targets">More spacing for small targets</h4>
  244. <p>If there are multiple actions on mobile and their size is 48px or less, then it makes sense to have more spacing between them.</p>
  245. <p><picture> <source srcset="https://ishadeed.com/_astro/small-target-spacing-1.cMIi5iEz_1xqhax.png" type="image/png"><source srcset="https://ishadeed.com/_astro/small-target-spacing-1.cMIi5iEz_2wprtu.webp" type="image/webp"> <img src="https://ishadeed.com/_astro/small-target-spacing-1.cMIi5iEz_1xqhax.png" alt="" loading="lazy" decoding="async"> </source></source></picture></p>
  246. <p>If the spacing is small, then the small targets will fail. Here is a figure
  247. on how they will look like.</p>
  248. <figure><picture> <source srcset="https://ishadeed.com/_astro/small-target-spacing-2.1zPSdkr3_3YhVL.png" type="image/png"><source srcset="https://ishadeed.com/_astro/small-target-spacing-2.1zPSdkr3_12XsfI.webp" type="image/webp"> <img src="https://ishadeed.com/_astro/small-target-spacing-2.1zPSdkr3_3YhVL.png" alt="" loading="lazy" decoding="async"> </source></source></picture> <figcaption>❌ Small targets need more spacing</figcaption></figure>
  249. <p>I have a real-life example that is very convincing. In Instagram, the comment’s like button is very small, but it works well because the spacing around it is large enough.</p>
  250. <p>See the following figure:</p>
  251. <p><picture> <source srcset="https://ishadeed.com/_astro/small-target-spacing-3.alOpO9pm_AloCY.png" type="image/png"><source srcset="https://ishadeed.com/_astro/small-target-spacing-3.alOpO9pm_1zkyVV.webp" type="image/webp"> <img src="https://ishadeed.com/_astro/small-target-spacing-3.alOpO9pm_AloCY.png" alt="" loading="lazy" decoding="async"> </source></source></picture></p>
  252. <p>The target sizes are below the minimum, but they work very well because
  253. the surrounding space is large enough.</p>
  254. <p>See the following figure for the highlighted space. The green highlight is just the space around the target.</p>
  255. <p><picture> <source srcset="https://ishadeed.com/_astro/small-target-spacing-4.gRQhUEl5_3GE4h.png" type="image/png"><source srcset="https://ishadeed.com/_astro/small-target-spacing-4.gRQhUEl5_12FOne.webp" type="image/webp"> <img src="https://ishadeed.com/_astro/small-target-spacing-4.gRQhUEl5_3GE4h.png" alt="" loading="lazy" decoding="async"> </source></source></picture></p>
  256. <h4 id="less-spacing-for-large-targets">Less spacing for large targets</h4>
  257. <p>In the following examples, the spacing between the targets is zero, but due to their large size, it’s fine to have less or no spacing at all.</p>
  258. <p><picture> <source srcset="https://ishadeed.com/_astro/large-target-spacing-1.V6iUc4le_ZKHYWq.png" type="image/png"><source srcset="https://ishadeed.com/_astro/large-target-spacing-1.V6iUc4le_dgalw.webp" type="image/webp"> <img src="https://ishadeed.com/_astro/large-target-spacing-1.V6iUc4le_ZKHYWq.png" alt="" loading="lazy" decoding="async"> </source></source></picture></p>
  259. <h4 id="target-size-that-grows-with-the-system-font-size">Target size that grows with the system font size</h4>
  260. <p>I did a test on Apple iOS to see if the system icons grow when I change the font size from the accessibility settings, and it did. If the user wants a bigger font, then the target size should grow proportionally for that.</p>
  261. <p><picture> <source srcset="https://ishadeed.com/_astro/large-target-spacing-2.iiO6ATQo_Z1Kl4JP.png" type="image/png"><source srcset="https://ishadeed.com/_astro/large-target-spacing-2.iiO6ATQo_ZLlTqS.webp" type="image/webp"> <img src="https://ishadeed.com/_astro/large-target-spacing-2.iiO6ATQo_Z1Kl4JP.png" alt="" loading="lazy" decoding="async"> </source></source></picture></p>
  262. <p>It’s not like we should do that for all apps, but it makes sense to think
  263. about it.</p>
  264. <h2 id="target-size-examples">Target size examples</h2>
  265. <p>As a front-end developer, it’s important to know the techniques to build an accessible target size.</p>
  266. <p>Let’s explore that in the following examples.</p>
  267. <h3 id="website-navigation">Website navigation</h3>
  268. <p>In this example, try to hover over a navigation item.</p>
  269. <p><astro-island uid="EoInw" prefix="r9" component-url="https://ishadeed.com/_astro/WebsiteNav.YZ97Zit8.js" component-export="default" renderer-url="https://ishadeed.com/_astro/client.2wOxL1Aq.js" props='{"helper":[0,true],"variation":[0,"1"]}' ssr="" client="visible" opts='{"name":"WebsiteNav","value":true}' await-children=""><div><p>Try to hover on an item ☝️</p><p></p></div></astro-island></p>
  270. <p>Notice how the clickable area is small? That happened because that spacing is added to the outer container, not the link itself.</p>
  271. <p><astro-island uid="40L80" prefix="r10" component-url="https://ishadeed.com/_astro/WebsiteNav.YZ97Zit8.js" component-export="default" renderer-url="https://ishadeed.com/_astro/client.2wOxL1Aq.js" props='{"content":[0,"Try to toggle the outline spacing, target area, or both!"],"variation":[0,"2"]}' ssr="" client="visible" opts='{"name":"WebsiteNav","value":true}' await-children=""><div><p>Try to toggle the outline spacing, target area, or both!</p></div></astro-island></p>
  272. <p>Let’s take a look at the CSS.</p>
  273. <pre class="language-css"><code is:raw="" class="language-css"><span class="token selector">.nav</span> <span class="token punctuation">{</span>
  274. <span class="token property">display</span><span class="token punctuation">:</span> flex<span class="token punctuation">;</span>
  275. <span class="token property">gap</span><span class="token punctuation">:</span> 1.5rem<span class="token punctuation">;</span>
  276. <span class="token property">padding-top</span><span class="token punctuation">:</span> 0.75rem<span class="token punctuation">;</span>
  277. <span class="token property">padding-bottom</span><span class="token punctuation">:</span> 0.75rem<span class="token punctuation">;</span>
  278. <span class="token punctuation">}</span></code></pre>
  279. <p>Notice that there is a:</p>
  280. <ul>
  281. <li>Gap between each nav item</li>
  282. <li>Vertical padding on the parent</li>
  283. </ul>
  284. <p>This isn’t good. We need to remove both and use padding on the individual navigation item.</p>
  285. <pre class="language-css"><code is:raw="" class="language-css"><span class="token selector">.nav</span> <span class="token punctuation">{</span>
  286. <span class="token property">display</span><span class="token punctuation">:</span> flex<span class="token punctuation">;</span>
  287. <span class="token punctuation">}</span>
  288. <span class="token selector">​ .nav__item a</span> <span class="token punctuation">{</span>
  289. <span class="token property">padding</span><span class="token punctuation">:</span> 0.75rem 1rem<span class="token punctuation">;</span>
  290. <span class="token punctuation">}</span></code></pre>
  291. <p><astro-island uid="rWMHG" prefix="r11" component-url="https://ishadeed.com/_astro/WebsiteNav.YZ97Zit8.js" component-export="default" renderer-url="https://ishadeed.com/_astro/client.2wOxL1Aq.js" props='{"content":[0,"Better now."],"variation":[0,"3"]}' ssr="" client="visible" opts='{"name":"WebsiteNav","value":true}' await-children=""></astro-island></p>
  292. <p>Hover over a navigation item to see the difference.</p>
  293. <h3 id="dropdown-toggle">Dropdown toggle</h3>
  294. <p>In this example, the target size is only on the label, not the whole menu container.</p>
  295. <p><astro-island uid="pgavt" prefix="r12" component-url="https://ishadeed.com/_astro/DropdownToggle.v46l7Fin.js" component-export="default" renderer-url="https://ishadeed.com/_astro/client.2wOxL1Aq.js" props='{"content":[0,"Try to hover on a menu ☝️"],"helper":[0,true],"variation":[0,"1"]}' ssr="" client="visible" opts='{"name":"DropdownToggle","value":true}' await-children=""><div><div class="example-wrapper center"><p class="_dropdown_ixlv5_1"><label for=":r12R0:">Category</label><select name="" id=":r12R0:"><option value="">Web development</option><option value="">Web design</option><option value="">CSS</option></select></p></div><p>Try to hover on a menu ☝️</p></div></astro-island></p>
  296. <p>The target area is only on the text, and it’s trimmed. To fix that, we need to expand the target area of the whole container.</p>
  297. <p>I added the padding to the <code>&lt;select&gt;</code> menu itself, instead of the outer wrapper.</p>
  298. <pre class="language-css"><code is:raw="" class="language-css"><span class="token selector">.dropdown__content</span> <span class="token punctuation">{</span>
  299. <span class="token property">padding</span><span class="token punctuation">:</span> 1.5rem 0.5rem<span class="token punctuation">;</span>
  300. <span class="token punctuation">}</span></code></pre>
  301. <p>Here is the <strong>enhanced</strong> example.</p>
  302. <p><astro-island uid="zgecT" prefix="r13" component-url="https://ishadeed.com/_astro/DropdownToggle.v46l7Fin.js" component-export="default" renderer-url="https://ishadeed.com/_astro/client.2wOxL1Aq.js" props='{"helper":[0,true],"content":[0,"Better, right?"],"variation":[0,"2"]}' ssr="" client="visible" opts='{"name":"DropdownToggle","value":true}' await-children=""><div><div class="example-wrapper center"><p class="_dropdown_ixlv5_1 _dropdownEnhanced_ixlv5_39"><label for=":r13R0:">Category</label><select name="" id=":r13R0:"><option value="">Web development</option><option value="">Web design</option><option value="">CSS</option></select></p></div><p>Better, right?</p></div></astro-island></p>
  303. <p>It’s just about adding the padding to the appropriate HTML element.</p>
  304. <h3 id="navigation-bar">Navigation bar</h3>
  305. <p>Here is an example where we have a bottom navigation for a website. Try to hover over an item to see its boundaries.</p>
  306. <p><astro-island uid="Zs8KPB" prefix="r84" component-url="https://ishadeed.com/_astro/BottomNavigation.wKeBXBWw.js" component-export="default" renderer-url="https://ishadeed.com/_astro/client.2wOxL1Aq.js" props='{"helper":[0,true]}' ssr="" client="visible" opts='{"name":"BottomNavigation","value":true}' await-children=""><div><div class="example-wrapper center"><div class="_box_myits_1"><div class="_content_myits_89"><h3>Try to tap on one of the navigation items</h3><p>This is a demo that shows the size of your finger when wearing a glove.</p></div></div></div><p>Try to hover on an item ☝️</p><p></p></div></astro-island></p>
  307. <p>Notice how the boundaries are small, compared to the space around the item itself.</p>
  308. <p>We can fix that in CSS by forcing each navigation item to fill the available width. To do that, we need to apply <code>flex: 1</code> on the navigation item.</p>
  309. <pre class="language-css"><code is:raw="" class="language-css"><span class="token selector">.nav</span> <span class="token punctuation">{</span>
  310. <span class="token property">display</span><span class="token punctuation">:</span> flex<span class="token punctuation">;</span>
  311. <span class="token punctuation">}</span>
  312. <span class="token selector">​ .nav__item</span> <span class="token punctuation">{</span>
  313. <span class="token property">flex</span><span class="token punctuation">:</span> 1<span class="token punctuation">;</span>
  314. <span class="token punctuation">}</span></code></pre>
  315. <p><astro-island uid="Z2ww7XC" prefix="r85" component-url="https://ishadeed.com/_astro/BottomNavigation.wKeBXBWw.js" component-export="default" renderer-url="https://ishadeed.com/_astro/client.2wOxL1Aq.js" props='{"variation":[0,"2"]}' ssr="" client="visible" opts='{"name":"BottomNavigation","value":true}' await-children=""><div><div class="example-wrapper center"><div class="_box_myits_1"><div class="_content_myits_89"><h3>Try to tap on one of the navigation items</h3><p>This is a demo that shows the size of your finger when wearing a glove.</p></div></div></div><p></p></div></astro-island></p>
  316. <p>Much better now.</p>
  317. <h3 id="icon-buttons">Icon buttons</h3>
  318. <p>Another example where having a large target size is important is an icon button.</p>
  319. <p>As a designer, you might need to use an icon only due to cases like not having enough space, for example, or if the icon is too common.</p>
  320. <p>In the following demo, try to hover over one of the icons and notice how the thumb size overlaps between them. For larger thumbs, the probability of tapping by mistake is high.</p>
  321. <p><astro-island uid="1FAgGj" prefix="r14" component-url="https://ishadeed.com/_astro/IconButtons.qKnNiHGe.js" component-export="default" renderer-url="https://ishadeed.com/_astro/client.2wOxL1Aq.js" props="{}" ssr="" client="visible" opts='{"name":"IconButtons","value":true}' await-children=""></astro-island></p>
  322. <p>To fix that, we need to do the following:</p>
  323. <ul>
  324. <li>Increase the spacing between the two buttons</li>
  325. <li>Increase the target size area</li>
  326. </ul>
  327. <pre class="language-css"><code is:raw="" class="language-css"><span class="token selector">.header-actions</span> <span class="token punctuation">{</span>
  328. <span class="token property">display</span><span class="token punctuation">:</span> flex<span class="token punctuation">;</span>
  329. <span class="token property">gap</span><span class="token punctuation">:</span> 1rem<span class="token punctuation">;</span>
  330. <span class="token punctuation">}</span>
  331. <span class="token selector">​ .action__item</span> <span class="token punctuation">{</span>
  332. <span class="token property">padding</span><span class="token punctuation">:</span> 1rem<span class="token punctuation">;</span>
  333. <span class="token punctuation">}</span></code></pre>
  334. <p><astro-island uid="Z6kBwC" prefix="r15" component-url="https://ishadeed.com/_astro/IconButtons.qKnNiHGe.js" component-export="default" renderer-url="https://ishadeed.com/_astro/client.2wOxL1Aq.js" props='{"variation":[0,"2"]}' ssr="" client="visible" opts='{"name":"IconButtons","value":true}' await-children=""></astro-island></p>
  335. <h3 id="safe-triangle-target-areas">Safe triangle target areas</h3>
  336. <p>It’s common to find multi-level menus on large websites like e-commerce apps, for example. Oftentimes, I find it hard to select an item from a multi-level menu, because the target area for the secondary menu is small.</p>
  337. <p>Let’s take the following example.</p>
  338. <p><picture> <source srcset="https://ishadeed.com/_astro/safe-triangle-1.gWdKi3yE_Z18UI7y.png" type="image/png"><source srcset="https://ishadeed.com/_astro/safe-triangle-1.gWdKi3yE_2gbY1b.webp" type="image/webp"> <img src="https://ishadeed.com/_astro/safe-triangle-1.gWdKi3yE_Z18UI7y.png" alt="" loading="lazy" decoding="async"> </source></source></picture></p>
  339. <p>Try to hover over any of the menu items with an arrow. Notice how the secondary
  340. menu appears only when you hover on its parent. If the mouse goes out of
  341. that area, the secondary menu will disappear.</p>
  342. <p><picture> <source srcset="https://ishadeed.com/_astro/safe-triangle-2.83z9uuii_ZXtSqJ.png" type="image/png"><source srcset="https://ishadeed.com/_astro/safe-triangle-2.83z9uuii_2qCNH0.webp" type="image/webp"> <img src="https://ishadeed.com/_astro/safe-triangle-2.83z9uuii_ZXtSqJ.png" alt="" loading="lazy" decoding="async"> </source></source></picture></p>
  343. <p>To fix that, we need to follow the safe triangle method that was seen <a href="https://height.app/blog/guide-to-build-context-menus">first
  344. on Amazon</a>.</p>
  345. <p>The idea is that instead of the highlighted target area, there will be a triangle attached to the sub-menu that will create a larger target size.</p>
  346. <p><picture> <source srcset="https://ishadeed.com/_astro/safe-triangle-3.KwHHoU15_1ATFa3.png" type="image/png"><source srcset="https://ishadeed.com/_astro/safe-triangle-3.KwHHoU15_Z49Kv9.webp" type="image/webp"> <img src="https://ishadeed.com/_astro/safe-triangle-3.KwHHoU15_1ATFa3.png" alt="" loading="lazy" decoding="async"> </source></source></picture></p>
  347. <p>How does that work in code? Let’s break this down.</p>
  348. <ul>
  349. <li>The submenu has a rectangle that is attached to it.</li>
  350. <li>That rectangle has clip-path, and the visible part of it is the triangle.</li>
  351. <li>Any area outside the triangle won’t receive pointer events due to its being clipped.</li>
  352. </ul>
  353. <h4 id="the-problem">The problem</h4>
  354. <p>We will use <code>clip-path</code> to create a triangle from the safe area element. That means we will need <strong>three points</strong>.</p>
  355. <p><picture> <source srcset="https://ishadeed.com/_astro/safe-triangle-4.VxSWoCWW_nAveQ.png" type="image/png"><source srcset="https://ishadeed.com/_astro/safe-triangle-4.VxSWoCWW_Z1hsUql.webp" type="image/webp"> <img src="https://ishadeed.com/_astro/safe-triangle-4.VxSWoCWW_nAveQ.png" alt="" loading="lazy" decoding="async"> </source></source></picture></p>
  356. <p><strong>The point that is close to the hand cursor is the dynamic one</strong> that
  357. we need to get. The other two points are static.</p>
  358. <pre class="language-css"><code is:raw="" class="language-css"><span class="token selector">.safeAreaElement</span> <span class="token punctuation">{</span>
  359. <span class="token property">clip-path</span><span class="token punctuation">:</span> <span class="token function">polygon</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--safe-start<span class="token punctuation">)</span><span class="token punctuation">,</span> 100% 100%<span class="token punctuation">,</span> 100% 0<span class="token punctuation">)</span><span class="token punctuation">;</span>
  360. <span class="token punctuation">}</span></code></pre>
  361. <h4 id="the-solution">The solution</h4>
  362. <p>The markup for this UI is as follows. We have a list item that contains a secondary menu. I wrote this in React, that’s why there are <code>className</code> and <code>ref={}</code>.</p>
  363. <pre class="language-html"><code is:raw="" class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>li</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>hasSub<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
  364. Sort by
  365. <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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>subMenu<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
  366. <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>ul</span><span class="token punctuation">&gt;</span></span>
  367. <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>li</span><span class="token punctuation">&gt;</span></span>Default<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>li</span><span class="token punctuation">&gt;</span></span>
  368. <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>li</span><span class="token punctuation">&gt;</span></span>Name<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>li</span><span class="token punctuation">&gt;</span></span>
  369. <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>li</span><span class="token punctuation">&gt;</span></span>Type<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>li</span><span class="token punctuation">&gt;</span></span>
  370. <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>li</span><span class="token punctuation">&gt;</span></span>Status<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>li</span><span class="token punctuation">&gt;</span></span>
  371. <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>ul</span><span class="token punctuation">&gt;</span></span>
  372. <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>span</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>safeAreaElement<span class="token punctuation">"</span></span> <span class="token attr-name">ref</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>{submenuSafeAreaRef}<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
  373. <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>span</span><span class="token punctuation">&gt;</span></span>
  374. <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
  375. <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>li</span><span class="token punctuation">&gt;</span></span></code></pre>
  376. <p>In CSS, I did the following:</p>
  377. <ul>
  378. <li>Added <code>position: absolute</code> to position the secondary menu to the right of its parent <code>&lt;li&gt;</code> item.</li>
  379. <li>Positioned the safe area element on the opposite side of the secondary menu.</li>
  380. <li>Added a default value of <code>clip-path</code>.</li>
  381. </ul>
  382. <pre class="language-css"><code is:raw="" class="language-css"><span class="token selector">.hasSub</span> <span class="token punctuation">{</span>
  383. <span class="token property">position</span><span class="token punctuation">:</span> relative<span class="token punctuation">;</span>
  384. <span class="token punctuation">}</span>
  385. <span class="token selector">​ .subMenu</span> <span class="token punctuation">{</span>
  386. <span class="token property">--safe-start</span><span class="token punctuation">:</span> 0% 0%<span class="token punctuation">;</span>
  387. <span class="token property">position</span><span class="token punctuation">:</span> absolute<span class="token punctuation">;</span>
  388. <span class="token property">left</span><span class="token punctuation">:</span> <span class="token function">calc</span><span class="token punctuation">(</span>100% - 8px<span class="token punctuation">)</span><span class="token punctuation">;</span>
  389. <span class="token property">top</span><span class="token punctuation">:</span> -2px<span class="token punctuation">;</span>
  390. <span class="token property">z-index</span><span class="token punctuation">:</span> 1<span class="token punctuation">;</span>
  391. <span class="token property">width</span><span class="token punctuation">:</span> 200px<span class="token punctuation">;</span>
  392. <span class="token punctuation">}</span>
  393. <span class="token selector">​ .safeAreaElement</span> <span class="token punctuation">{</span>
  394. <span class="token property">position</span><span class="token punctuation">:</span> absolute<span class="token punctuation">;</span>
  395. <span class="token property">top</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span>
  396. <span class="token property">bottom</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span>
  397. <span class="token property">right</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span>
  398. <span class="token property">width</span><span class="token punctuation">:</span> <span class="token function">calc</span><span class="token punctuation">(</span>100% - 8px<span class="token punctuation">)</span><span class="token punctuation">;</span>
  399. <span class="token property">clip-path</span><span class="token punctuation">:</span> <span class="token function">polygon</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--safe-start<span class="token punctuation">)</span><span class="token punctuation">,</span> 100% 100%<span class="token punctuation">,</span> 100% 0<span class="token punctuation">)</span><span class="token punctuation">;</span>
  400. <span class="token punctuation">}</span></code></pre>
  401. <p>To make sure we’re on the same page, here is the annotation of the HTML parts.</p>
  402. <p><picture> <source srcset="https://ishadeed.com/_astro/safe-triangle-5.ybtFLCHC_ZMUuo2.png" type="image/png"><source srcset="https://ishadeed.com/_astro/safe-triangle-5.ybtFLCHC_Z2sYV4e.webp" type="image/webp"> <img src="https://ishadeed.com/_astro/safe-triangle-5.ybtFLCHC_ZMUuo2.png" alt="" loading="lazy" decoding="async"> </source></source></picture></p>
  403. <p>Also, here is what the default clipped area looks like.</p>
  404. <p><picture> <source srcset="https://ishadeed.com/_astro/safe-triangle-6.I2btTKYg_1DzkWG.png" type="image/png"><source srcset="https://ishadeed.com/_astro/safe-triangle-6.I2btTKYg_Z1u5Hv.webp" type="image/webp"> <img src="https://ishadeed.com/_astro/safe-triangle-6.I2btTKYg_1DzkWG.png" alt="" loading="lazy" decoding="async"> </source></source></picture></p>
  405. <h4 id="the-safe-triangle-area">The safe triangle area</h4>
  406. <p>The safe area is positioned relative to the secondary menu. It’s like an extension to the menu.</p>
  407. <p><picture> <source srcset="https://ishadeed.com/_astro/safe-triangle-7.2qp5GWHQ_Z13SblS.png" type="image/png"><source srcset="https://ishadeed.com/_astro/safe-triangle-7.2qp5GWHQ_2levLQ.webp" type="image/webp"> <img src="https://ishadeed.com/_astro/safe-triangle-7.2qp5GWHQ_Z13SblS.png" alt="" loading="lazy" decoding="async"> </source></source></picture></p>
  408. <h4 id="get-the-mouse-x--y-coordinates">Get the mouse X &amp; Y coordinates</h4>
  409. <p>When the user hovers in the safe area, we need to get the mouse X &amp; Y coordinates. In Javascript, we get the X &amp; Y values for the whole page, so we need to scope them for the safe area element only.</p>
  410. <pre class="language-js"><code is:raw="" class="language-js">
  411. <span class="token keyword">const</span> <span class="token punctuation">{</span>
  412. <span class="token literal-property property">width</span><span class="token operator">:</span> menuItemWidth<span class="token punctuation">,</span>
  413. <span class="token literal-property property">height</span><span class="token operator">:</span> menuItemHeight<span class="token punctuation">,</span>
  414. <span class="token punctuation">}</span> <span class="token operator">=</span> hasSubRef<span class="token punctuation">.</span>current<span class="token punctuation">.</span><span class="token function">getBoundingClientRect</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
  415. <span class="token keyword">const</span> <span class="token punctuation">{</span>
  416. <span class="token literal-property property">left</span><span class="token operator">:</span> safeAreaLeftPos<span class="token punctuation">,</span>
  417. <span class="token literal-property property">top</span><span class="token operator">:</span> safeAreaTopPos<span class="token punctuation">,</span>
  418. <span class="token literal-property property">width</span><span class="token operator">:</span> safeAreaWidth<span class="token punctuation">,</span>
  419. <span class="token literal-property property">height</span><span class="token operator">:</span> safeAreaHeight<span class="token punctuation">,</span>
  420. <span class="token punctuation">}</span> <span class="token operator">=</span> submenuSafeAreaRef<span class="token punctuation">.</span>current<span class="token punctuation">.</span><span class="token function">getBoundingClientRect</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
  421. <span class="token keyword">const</span> localX <span class="token operator">=</span> mouseX <span class="token operator">-</span> safeAreaLeftPos
  422. <span class="token keyword">const</span> localY <span class="token operator">=</span> mouseY <span class="token operator">-</span> safeAreaTopPos</code></pre>
  423. <p>In the code, we got:</p>
  424. <ul>
  425. <li>Width and height of the <code>&lt;li&gt;</code></li>
  426. <li>Left and top position of the safe area relative to the page</li>
  427. <li>The mouse X and Y then subtract the safe area left and top from each, respectively.</li>
  428. </ul>
  429. <p>This will give us the local coordinates scoped to the safe area.</p>
  430. <p><picture> <source srcset="https://ishadeed.com/_astro/safe-triangle-8.vEvvLEEa_17eNqj.png" type="image/png"><source srcset="https://ishadeed.com/_astro/safe-triangle-8.vEvvLEEa_ZxOCeS.webp" type="image/webp"> <img src="https://ishadeed.com/_astro/safe-triangle-8.vEvvLEEa_17eNqj.png" alt="" loading="lazy" decoding="async"> </source></source></picture></p>
  431. <p>Given the data we have now, we need to do a few more steps to get the X
  432. and Y percentage values to use in the <code>clip-path</code>.</p>
  433. <ul>
  434. <li>Any X value that is larger than the safe area element width isn’t needed</li>
  435. <li>Any Y value below the main <code>&lt;li&gt;</code> isn’t needed (the black line)</li>
  436. <li>Convert those X and Y values into percentages by dividing them with the width and height of the safe area element, respectively.</li>
  437. </ul>
  438. <p><picture> <source srcset="https://ishadeed.com/_astro/safe-triangle-9.bgggmP8x_201eKf.png" type="image/png"><source srcset="https://ishadeed.com/_astro/safe-triangle-9.bgggmP8x_jVN53.webp" type="image/webp"> <img src="https://ishadeed.com/_astro/safe-triangle-9.bgggmP8x_201eKf.png" alt="" loading="lazy" decoding="async"> </source></source></picture></p>
  439. <p>Take a look at the Javascript.</p>
  440. <pre class="language-js"><code is:raw="" class="language-js">
  441. <span class="token keyword">if</span> <span class="token punctuation">(</span>
  442. localX <span class="token operator">&gt;</span> <span class="token number">0</span> <span class="token operator">&amp;&amp;</span>
  443. localX <span class="token operator">&lt;</span> menuItemWidth <span class="token operator">&amp;&amp;</span>
  444. localY <span class="token operator">&gt;</span> <span class="token number">0</span> <span class="token operator">&amp;&amp;</span>
  445. localY <span class="token operator">&lt;</span> menuItemHeight
  446. <span class="token punctuation">)</span> <span class="token punctuation">{</span>
  447. <span class="token keyword">const</span> percentageX <span class="token operator">=</span> <span class="token punctuation">(</span>localX <span class="token operator">/</span> safeAreaWidth<span class="token punctuation">)</span> <span class="token operator">*</span> <span class="token number">100</span><span class="token punctuation">;</span>
  448. <span class="token keyword">const</span> percentageY <span class="token operator">=</span> <span class="token punctuation">(</span>localY <span class="token operator">/</span> safeAreaHeight<span class="token punctuation">)</span> <span class="token operator">*</span> <span class="token number">100</span><span class="token punctuation">;</span>
  449. submenuRef<span class="token punctuation">.</span>current<span class="token punctuation">.</span>style<span class="token punctuation">.</span><span class="token function">setProperty</span><span class="token punctuation">(</span>
  450. <span class="token string">"--safe-start"</span><span class="token punctuation">,</span>
  451. <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>percentageX<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">% </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>percentageY<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">%</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">,</span>
  452. <span class="token punctuation">)</span><span class="token punctuation">;</span>
  453. <span class="token punctuation">}</span></code></pre>
  454. <p>The magic here happens because <code>clip-path</code> disables pointer events for clipped areas. When we hover under the menu, it disappears as expected.</p>
  455. <p>Explore the interactive demo below and try to move the dynamic path point.</p>
  456. <p><astro-island uid="1pnaJG" prefix="r16" component-url="https://ishadeed.com/_astro/SafeTrianglePlay.JCL8WTQn.js" component-export="default" renderer-url="https://ishadeed.com/_astro/client.2wOxL1Aq.js" props="{}" ssr="" client="visible" opts='{"name":"SafeTrianglePlay","value":true}' await-children=""><div><p class="_tabsWrapper_14lhn_134"><button class="_button_14lhn_139 _isActive_14lhn_152">Demo</button><button class="_button_14lhn_139">Video</button></p><div><div><div class="_safeTestPlay_14lhn_1"><ul><li>New project</li><li class="has-sub">Change workflow<span>➡️</span></li><li>Sort by</li><li>Filter by</li><li>Another item</li></ul><p class="_pathResult_14lhn_123">clip-path: polygon(<code>0% 0%</code>, <code>100% 100%</code>,<code>100% 0</code>);</p></div></div><p><video poster="https://ishadeed.com/assets/target-size/videos/safe-tri-1.png" src="https://ishadeed.com/assets/target-size/videos/safe-tri-1.mp4" controls="" controlslist="nodownload"></video></p></div></div></astro-island></p>
  457. <p>Let’s explore a demo that uses the safe triangle method. First, try to hover with the default state, then change it to the safe area.</p>
  458. <p><astro-island uid="1YW1aH" prefix="r17" component-url="https://ishadeed.com/_astro/SafeTriangle.nCpnxMOa.js" component-export="default" renderer-url="https://ishadeed.com/_astro/client.2wOxL1Aq.js" props='{"name":[0,"wrapper 2"]}' ssr="" client="visible" opts='{"name":"SafeTriangle","value":true}' await-children=""><div><p class="_tabsWrapper_18mju_113"><button class="_button_18mju_118 _isActive_18mju_131">Demo</button><button class="_button_18mju_118">Video</button></p><div><div><div class="_safeTest_18mju_1"><ul><li>New project</li><li class="_hasSub_18mju_94 _hasSubActive_18mju_97">Change workflow<span>➡️</span></li><li>Sort by</li><li>Filter by</li><li>Another item</li></ul></div></div><p><video poster="https://ishadeed.com/assets/target-size/videos/safe-tri-2.png" src="https://ishadeed.com/assets/target-size/videos/safe-tri-2.mp4" controls="" controlslist="nodownload"></video></p></div></div></astro-island></p>
  459. <h3 id="mobile-search">Mobile search</h3>
  460. <p>While researching, I found an interesting target size issue in a couple of websites. When the search is active, there are no clear boundaries for it. That causes the user to guess where to tab.</p>
  461. <p>In the following example, the search is shown once the mobile menu is toggled. However, it’s not clear where the boundaries of the search are.</p>
  462. <p><picture> <source srcset="https://ishadeed.com/_astro/mobile-search-1.lhfbhihy_d0grH.png" type="image/png"><source srcset="https://ishadeed.com/_astro/mobile-search-1.lhfbhihy_Z1s4adu.webp" type="image/webp"> <img src="https://ishadeed.com/_astro/mobile-search-1.lhfbhihy_d0grH.png" alt="" loading="lazy" decoding="async"> </source></source></picture></p>
  463. <p>It’s not a good idea to let the user “guess” where the target area is.
  464. In this design, here is a comparison <strong>between my expectation and the actual
  465. target size</strong>.</p>
  466. <p><picture> <source srcset="https://ishadeed.com/_astro/mobile-search-2.6fdQVi3Q_1aSf9Q.png" type="image/png"><source srcset="https://ishadeed.com/_astro/mobile-search-2.6fdQVi3Q_Zubbvl.webp" type="image/webp"> <img src="https://ishadeed.com/_astro/mobile-search-2.6fdQVi3Q_1aSf9Q.png" alt="" loading="lazy" decoding="async"> </source></source></picture></p>
  467. <p>See the following demo. It’s confusing.</p>
  468. <p><astro-island uid="rTo2R" prefix="r18" component-url="https://ishadeed.com/_astro/MobileSearch.ntbnwgyB.js" component-export="default" renderer-url="https://ishadeed.com/_astro/client.2wOxL1Aq.js" props='{"helper":[0,true],"content":[0,"Toggle the checkbox and see how the search makes you think that it spans to the far right."]}' ssr="" client="visible" opts='{"name":"MobileSearch","value":true}' await-children=""><div><div class="example-wrapper center _wrapper_hbbaq_1"><p class="checkbox"><input id=":r18R5:" type="checkbox" class="sr-only _input_3qp17_1" checked><label class="_label_3qp17_16" for=":r18R5:">Toggle highlight</label></p><div class="_page_hbbaq_7 false"><div><p>Random title</p><p> This is a sample text. CSS is awesome.</p></div></div></div><p>Toggle the checkbox and see how the search makes you think that it spans to the far right.</p></div></astro-island></p>
  469. <p>To solve that, we need to expand the target area and make it fill the rest of the space.</p>
  470. <pre class="language-css"><code is:raw="" class="language-css"><span class="token selector">.search-form</span> <span class="token punctuation">{</span>
  471. <span class="token property">position</span><span class="token punctuation">:</span> absolute<span class="token punctuation">;</span>
  472. <span class="token property">top</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span>
  473. <span class="token property">bottom</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span>
  474. <span class="token property">right</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span>
  475. <span class="token punctuation">}</span>
  476. <span class="token selector">.search-form input</span> <span class="token punctuation">{</span>
  477. <span class="token property">width</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span>
  478. <span class="token property">height</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span>
  479. <span class="token punctuation">}</span></code></pre>
  480. <p><astro-island uid="ZwgKg2" prefix="r19" component-url="https://ishadeed.com/_astro/MobileSearch.ntbnwgyB.js" component-export="default" renderer-url="https://ishadeed.com/_astro/client.2wOxL1Aq.js" props='{"helper":[0,true],"content":[0,"The search is positioned absolutely to the header with a limited width."],"variation":[0,"enhanced"]}' ssr="" client="visible" opts='{"name":"MobileSearch","value":true}' await-children=""><div><div class="example-wrapper center _wrapper_hbbaq_1"><p class="checkbox"><input id=":r19R5:" type="checkbox" class="sr-only _input_3qp17_1" checked><label class="_label_3qp17_16" for=":r19R5:">Toggle highlight</label></p><div class="_page_hbbaq_7 _enhanced_hbbaq_109"><div><p>Random title</p><p> This is a sample text. CSS is awesome.</p></div></div></div><p>The search is positioned absolutely to the header with a limited width.</p></div></astro-island></p>
  481. <h3 id="active-navigation-border">Active Navigation border</h3>
  482. <p>This is an example of a navigation where the hovered item shows a bottom border. When you see that, you assume that the whole area is clickable.</p>
  483. <p><astro-island uid="1SK72H" prefix="r20" component-url="https://ishadeed.com/_astro/NavBottomBorder.rxRKGiDC.js" component-export="default" renderer-url="https://ishadeed.com/_astro/client.2wOxL1Aq.js" props='{"helper":[0,true],"variation":[0,"1"]}' ssr="" client="visible" opts='{"name":"NavBottomBorder","value":true}' await-children=""><div><p>Try to hover on an item ☝️</p></div></astro-island></p>
  484. <p>However, when you try to hover over another item, you will discover that the target area is only for the text. The border is just for visual purposes. Notice the outline around the inner item.</p>
  485. <p><picture> <source srcset="https://ishadeed.com/_astro/nav-bottom-border.oj4vN2U9_1WWwv8.png" type="image/png"><source srcset="https://ishadeed.com/_astro/nav-bottom-border.oj4vN2U9_OWXam.webp" type="image/webp"> <img src="https://ishadeed.com/_astro/nav-bottom-border.oj4vN2U9_1WWwv8.png" alt="" loading="lazy" decoding="async"> </source></source></picture></p>
  486. <p>The problem is that there is horizontal padding on the <code>&lt;li&gt;</code> element, and the <code>&lt;a&gt;</code> has vertical padding only.</p>
  487. <pre class="language-css"><code is:raw="" class="language-css"><span class="token selector">.nav-item</span> <span class="token punctuation">{</span>
  488. <span class="token property">padding-left</span><span class="token punctuation">:</span> 1rem<span class="token punctuation">;</span>
  489. <span class="token property">padding-right</span><span class="token punctuation">:</span> 1rem<span class="token punctuation">;</span>
  490. <span class="token property">border-bottom</span><span class="token punctuation">:</span> 4px solid transparent<span class="token punctuation">;</span>
  491. <span class="token punctuation">}</span>
  492. <span class="token selector">​ .nav-item a</span> <span class="token punctuation">{</span>
  493. <span class="token property">padding-top</span><span class="token punctuation">:</span> 1rem<span class="token punctuation">;</span>
  494. <span class="token property">padding-bottom</span><span class="token punctuation">:</span> 1rem<span class="token punctuation">;</span>
  495. <span class="token punctuation">}</span>
  496. <span class="token selector">​ .nav-item:hover</span> <span class="token punctuation">{</span>
  497. <span class="token property">border-bottom-color</span><span class="token punctuation">:</span> red<span class="token punctuation">;</span>
  498. <span class="token punctuation">}</span></code></pre>
  499. <p>To fix that, we need to add all the padding to the <code>&lt;a&gt;</code> element.</p>
  500. <pre class="language-css"><code is:raw="" class="language-css"><span class="token selector">.nav-item</span> <span class="token punctuation">{</span>
  501. <span class="token property">border-bottom</span><span class="token punctuation">:</span> 4px solid transparent<span class="token punctuation">;</span>
  502. <span class="token punctuation">}</span>
  503. <span class="token selector">​ .nav-item a</span> <span class="token punctuation">{</span>
  504. <span class="token property">padding</span><span class="token punctuation">:</span> 1rem<span class="token punctuation">;</span>
  505. <span class="token punctuation">}</span>
  506. <span class="token selector">​ .nav-item:hover</span> <span class="token punctuation">{</span>
  507. <span class="token property">border-bottom-color</span><span class="token punctuation">:</span> red<span class="token punctuation">;</span>
  508. <span class="token punctuation">}</span></code></pre>
  509. <p>See it yourself in the following demo.</p>
  510. <p><astro-island uid="EyAQd" prefix="r21" component-url="https://ishadeed.com/_astro/NavBottomBorder.rxRKGiDC.js" component-export="default" renderer-url="https://ishadeed.com/_astro/client.2wOxL1Aq.js" props='{"helper":[0,true],"variation":[0,"2"]}' ssr="" client="visible" opts='{"name":"NavBottomBorder","value":true}' await-children=""><div><p>Try to hover on an item ☝️</p></div></astro-island></p>
  511. <p>Much better. Now the whole link is interacive.</p>
  512. <h3 id="player-ui">Player UI</h3>
  513. <p>In this example, we have an audio player UI.</p>
  514. <p>The target area is limited to the bar height only.</p>
  515. <p>We need to fix that by extending the target size to cover at least the playhead (the little circle) height. In the following figure, the target size should be within the top and bottom red lines.</p>
  516. <p><picture> <source srcset="https://ishadeed.com/_astro/player-ui-1.Xn-WLRZ-_1luvnd.png" type="image/png"><source srcset="https://ishadeed.com/_astro/player-ui-1.Xn-WLRZ-_Z1sv3Bu.webp" type="image/webp"> <img src="https://ishadeed.com/_astro/player-ui-1.Xn-WLRZ-_1luvnd.png" alt="" loading="lazy" decoding="async"> </source></source></picture>A simple
  517. thing to do is to wrap the progress in a container and extend the
  518. height of it. That way, the progress height will remain the same, but
  519. the clickable area will get bigger.</p>
  520. <p>See the following HTML:</p>
  521. <pre class="language-html"><code is:raw="" class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</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>progress<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
  522. <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</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>progressBar<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
  523. <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span></code></pre>
  524. <p>Please note this HTML is just for demo purposes. There are a lot of things missing like the current progress bar value.</p>
  525. <p>Here is the CSS:</p>
  526. <pre class="language-css"><code is:raw="" class="language-css"><span class="token selector">.progress</span> <span class="token punctuation">{</span>
  527. <span class="token property">flex</span><span class="token punctuation">:</span> auto<span class="token punctuation">;</span>
  528. <span class="token property">display</span><span class="token punctuation">:</span> flex<span class="token punctuation">;</span>
  529. <span class="token property">align-items</span><span class="token punctuation">:</span> center<span class="token punctuation">;</span>
  530. <span class="token property">height</span><span class="token punctuation">:</span> 16px<span class="token punctuation">;</span>
  531. <span class="token property">cursor</span><span class="token punctuation">:</span> pointer<span class="token punctuation">;</span>
  532. <span class="token punctuation">}</span>
  533. <span class="token selector">.progressBar</span> <span class="token punctuation">{</span>
  534. <span class="token property">flex</span><span class="token punctuation">:</span> auto<span class="token punctuation">;</span>
  535. <span class="token property">height</span><span class="token punctuation">:</span> 4px<span class="token punctuation">;</span>
  536. <span class="token property">background-color</span><span class="token punctuation">:</span> <span class="token function">color-mix</span><span class="token punctuation">(</span>in srgb<span class="token punctuation">,</span> #9c3ce7<span class="token punctuation">,</span> white 70%<span class="token punctuation">)</span><span class="token punctuation">;</span>
  537. <span class="token punctuation">}</span></code></pre>
  538. <p>It’s much better now. Try to click anywhere on the progress bar. Play with the checkbox to see the before &amp; after.</p>
  539. <p><astro-island uid="Z1P9OSf" prefix="r22" component-url="https://ishadeed.com/_astro/PlayerUI.ZnDFoBQF.js" component-export="default" renderer-url="https://ishadeed.com/_astro/client.2wOxL1Aq.js" props="{}" ssr="" client="visible" opts='{"name":"PlayerUI","value":true}' await-children=""></astro-island></p>
  540. <h3 id="avoid-dead-target-areas">Avoid dead target areas</h3>
  541. <p>A dead area is when there are multiple ways to do the same action. This can apply to a label and an icon.</p>
  542. <p>In the following example, there are two target areas (label + icon). Between them, there is a dead target area; when clicked or tapped, the action won’t be triggered.</p>
  543. <p><astro-island uid="ma1lq" prefix="r23" component-url="https://ishadeed.com/_astro/DeadAreas.ovHoBlHE.js" component-export="default" renderer-url="https://ishadeed.com/_astro/client.2wOxL1Aq.js" props='{"helper":[0,true]}' ssr="" client="visible" opts='{"name":"DeadArea","value":true}' await-children=""><div><p class="example-wrapper center _wrapper_1e53f_1"></p><p>Try to hover between the label and icon</p></div></astro-island></p>
  544. <p>When the same target areas refer to the same action, it’s good to keep them as one target, or at least to remove the gap between them.</p>
  545. <p>In the following demo, I just removed the gap and the issue is fixed. Unless there is no real need for that, I would recommend combining them as one action.</p>
  546. <p><astro-island uid="Z1KwDX7" prefix="r24" component-url="https://ishadeed.com/_astro/DeadAreas.ovHoBlHE.js" component-export="default" renderer-url="https://ishadeed.com/_astro/client.2wOxL1Aq.js" props='{"variation":[0,"enhanced"]}' ssr="" client="visible" opts='{"name":"DeadArea","value":true}' await-children=""></astro-island></p>
  547. <h3 id="extend-target-size-with-pseudo-elements">Extend target size with pseudo-elements</h3>
  548. <p>This is a useful technique that can increase the target size without changing the object size or padding. Here is a summary:</p>
  549. <ul>
  550. <li>Add a pseudo-element to the target</li>
  551. <li>Make it size larger than the target</li>
  552. <li>Done! 🥳</li>
  553. </ul>
  554. <p>In the following example, we have a simple link. Notice that the target area is highlighted.</p>
  555. <p><astro-island uid="ZpaUjB" prefix="r25" component-url="https://ishadeed.com/_astro/ExpandTarget.UVLz_TuK.js" component-export="default" renderer-url="https://ishadeed.com/_astro/client.2wOxL1Aq.js" props='{"variation":[0,"1"],"helper":[0,true]}' ssr="" client="visible" opts='{"name":"ExpandTarget","value":true}' await-children=""></astro-island></p>
  556. <p>The hover effect is simple. Try to hover and see it.</p>
  557. <p>When we add a pseudo-element to the link, it will take the same target area as its parent. That means we can extend the target area in a very dynamic way.</p>
  558. <p>Try to hover over the purple square, and notice how it will trigger the hover effect on the link (its parent).</p>
  559. <pre class="language-css"><code is:raw="" class="language-css"><span class="token selector">.link</span> <span class="token punctuation">{</span>
  560. <span class="token property">position</span><span class="token punctuation">:</span> relative<span class="token punctuation">;</span>
  561. <span class="token punctuation">}</span>
  562. <span class="token selector">.link a:after</span> <span class="token punctuation">{</span>
  563. <span class="token property">content</span><span class="token punctuation">:</span> <span class="token string">""</span><span class="token punctuation">;</span>
  564. <span class="token property">position</span><span class="token punctuation">:</span> absolute<span class="token punctuation">;</span>
  565. <span class="token property">left</span><span class="token punctuation">:</span> 160%<span class="token punctuation">;</span>
  566. <span class="token property">top</span><span class="token punctuation">:</span> -40px<span class="token punctuation">;</span>
  567. <span class="token property">--size</span><span class="token punctuation">:</span> 3rem<span class="token punctuation">;</span>
  568. <span class="token property">width</span><span class="token punctuation">:</span> <span class="token function">var</span><span class="token punctuation">(</span>--size<span class="token punctuation">)</span><span class="token punctuation">;</span>
  569. <span class="token property">height</span><span class="token punctuation">:</span> <span class="token function">var</span><span class="token punctuation">(</span>--size<span class="token punctuation">)</span><span class="token punctuation">;</span>
  570. <span class="token property">background-color</span><span class="token punctuation">:</span> <span class="token function">rgb</span><span class="token punctuation">(</span>194<span class="token punctuation">,</span> 170<span class="token punctuation">,</span> 216<span class="token punctuation">)</span><span class="token punctuation">;</span>
  571. <span class="token property">border-radius</span><span class="token punctuation">:</span> 15px<span class="token punctuation">;</span>
  572. <span class="token punctuation">}</span></code></pre>
  573. <p><astro-island uid="1WDIR5" prefix="r26" component-url="https://ishadeed.com/_astro/ExpandTarget.UVLz_TuK.js" component-export="default" renderer-url="https://ishadeed.com/_astro/client.2wOxL1Aq.js" props='{"content":[0,"Try to hover on the purple square. It will trigger the link."],"variation":[0,"2"],"helper":[0,true]}' ssr="" client="visible" opts='{"name":"ExpandTarget","value":true}' await-children=""><div><p>Try to hover on the purple square. It will trigger the link.</p></div></astro-island></p>
  574. <p>Interesting, right? We can take this even further and have two pseudo-elements (<code>:before</code> and <code>:after</code>).</p>
  575. <p>This technique can be extended to create more interesting interactions. See the following example.</p>
  576. <p><astro-island uid="Zm2kOJ" prefix="r27" component-url="https://ishadeed.com/_astro/ExpandTargetExample.TjNPOfag.js" component-export="default" renderer-url="https://ishadeed.com/_astro/client.2wOxL1Aq.js" props="{}" ssr="" client="visible" opts='{"name":"ExpandTargetExample","value":true}' await-children=""></astro-island></p>
  577. <p>When you hover over one of the links, its thumbnail will be triggered. Interesting, right? I used my <a href="https://debuggingcss.com/">Debugging CSS</a> book and <a href="https://defensivecss.dev/">Defensive CSS</a> as an example.</p>
  578. <p>Here is how I did it:</p>
  579. <ul>
  580. <li>There is a <code>&lt;p&gt;</code> element with two links.</li>
  581. <li>Each link has a thumb that is positioned to the right side.</li>
  582. <li>There is a large padding on the right to accommodate the thumbs.</li>
  583. <li>When the link is hovered, its pseudo-element will be scaled and positioned on top.</li>
  584. </ul>
  585. <pre class="language-css"><code is:raw="" class="language-css"><span class="token selector">p a:after</span> <span class="token punctuation">{</span>
  586. <span class="token property">content</span><span class="token punctuation">:</span> <span class="token string">""</span><span class="token punctuation">;</span>
  587. <span class="token property">position</span><span class="token punctuation">:</span> absolute<span class="token punctuation">;</span>
  588. <span class="token property">--size</span><span class="token punctuation">:</span> 150px<span class="token punctuation">;</span>
  589. <span class="token property">right</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span>
  590. <span class="token property">width</span><span class="token punctuation">:</span> <span class="token function">var</span><span class="token punctuation">(</span>--size<span class="token punctuation">)</span><span class="token punctuation">;</span>
  591. <span class="token property">height</span><span class="token punctuation">:</span> <span class="token function">var</span><span class="token punctuation">(</span>--size<span class="token punctuation">)</span><span class="token punctuation">;</span>
  592. <span class="token property">background-color</span><span class="token punctuation">:</span> red<span class="token punctuation">;</span>
  593. <span class="token property">transition</span><span class="token punctuation">:</span> 0.3s ease-out<span class="token punctuation">;</span>
  594. <span class="token punctuation">}</span>
  595. <span class="token selector">p a:hover:after</span> <span class="token punctuation">{</span>
  596. <span class="token property">transform</span><span class="token punctuation">:</span> <span class="token function">scale</span><span class="token punctuation">(</span>1.3<span class="token punctuation">)</span><span class="token punctuation">;</span>
  597. <span class="token punctuation">}</span>
  598. <span class="token selector">p .debugging:after</span> <span class="token punctuation">{</span>
  599. <span class="token property">right</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span>
  600. <span class="token punctuation">}</span>
  601. <span class="token selector">p .defensive:after</span> <span class="token punctuation">{</span>
  602. <span class="token property">right</span><span class="token punctuation">:</span> 200px<span class="token punctuation">;</span>
  603. <span class="token punctuation">}</span></code></pre>
  604. <p>With that in mind, let’s explore a few examples of using pseudo-elements to increase the target size.</p>
  605. <h4 id="card-block-link">Card block link</h4>
  606. <p>A common confusion that happens on the web is that you have a certain expectation for where the target area for a card is, and when you try to interact with it, it’s not as per your expectations.</p>
  607. <p>Using this solution will make the text hard to select. Use with caution and when necessary only.</p>
  608. <p>Consider the following example. Try to hover on the card and notice that only the title is clickable.</p>
  609. <p><astro-island uid="alkhL" prefix="r28" component-url="https://ishadeed.com/_astro/Card.HXVEtCTV.js" component-export="default" renderer-url="https://ishadeed.com/_astro/client.2wOxL1Aq.js" props="{}" ssr="" client="visible" opts='{"name":"Card","value":true}' await-children=""></astro-island></p>
  610. <p>The clickable area is only in the title.</p>
  611. <p><picture> <source srcset="https://ishadeed.com/_astro/expand-target-card-2.PTN6qcho_ZvQD7A.png" type="image/png"><source srcset="https://ishadeed.com/_astro/expand-target-card-2.PTN6qcho_ZiePUN.webp" type="image/webp"> <img src="https://ishadeed.com/_astro/expand-target-card-2.PTN6qcho_ZvQD7A.png" alt="" loading="lazy" decoding="async"> </source></source></picture></p>
  612. <p>Here is the card’s HTML markup.</p>
  613. <pre class="language-html"><code is:raw="" class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>article</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>card<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
  614. <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>img</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>cookies.jpg<span class="token punctuation">"</span></span> <span class="token attr-name">alt</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">/&gt;</span></span>
  615. <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span><span class="token punctuation">&gt;</span></span>
  616. <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h3</span><span class="token punctuation">&gt;</span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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">&gt;</span></span>How I made cookies<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">&gt;</span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h3</span><span class="token punctuation">&gt;</span></span>
  617. <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>time</span><span class="token punctuation">&gt;</span></span>2 Dec 2023<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>time</span><span class="token punctuation">&gt;</span></span>
  618. <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
  619. <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>article</span><span class="token punctuation">&gt;</span></span></code></pre>
  620. <p>Notice that the link is inside the <code>&lt;h3&gt;</code> element, so that’s why the target area is there only.</p>
  621. <p>We can add a pseudo-element to the <code>&lt;a&gt;</code> element, and position it to cover the card. Try to hover over the card, now all the cards are clickable.</p>
  622. <pre class="language-css"><code is:raw="" class="language-css"><span class="token selector">.card</span> <span class="token punctuation">{</span>
  623. <span class="token property">position</span><span class="token punctuation">:</span> relative<span class="token punctuation">;</span>
  624. <span class="token punctuation">}</span>
  625. <span class="token selector">​ .card h3 a::after</span> <span class="token punctuation">{</span>
  626. <span class="token property">content</span><span class="token punctuation">:</span> <span class="token string">""</span><span class="token punctuation">;</span>
  627. <span class="token property">position</span><span class="token punctuation">:</span> absolute<span class="token punctuation">;</span>
  628. <span class="token property">inset</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span>
  629. <span class="token punctuation">}</span></code></pre>
  630. <p><astro-island uid="Z1W7Ryd" prefix="r29" component-url="https://ishadeed.com/_astro/Card.HXVEtCTV.js" component-export="default" renderer-url="https://ishadeed.com/_astro/client.2wOxL1Aq.js" props='{"enhanced":[0,true]}' ssr="" client="visible" opts='{"name":"Card","value":true}' await-children=""></astro-island></p>
  631. <p>In this example, there is not much text, so the accessibility concern of text being not selectable might not be valid.</p>
  632. <p>However, I would be careful to use this solution if there is too much text in the card (e.g.: title, description, author name.. etc).</p>
  633. <p>Another use case for increasing the target size via pseudo-element is a mobile menu. I already showed a similar example but solved it via padding.</p>
  634. <p>See the following demo.</p>
  635. <p><astro-island uid="ZFnSBW" prefix="r30" component-url="https://ishadeed.com/_astro/MobileMenu.jhXdaTWo.js" component-export="default" renderer-url="https://ishadeed.com/_astro/client.2wOxL1Aq.js" props="{}" ssr="" client="visible" opts='{"name":"MobileMenu","value":true}' await-children=""></astro-island></p>
  636. <p>The target size is small. You need to point the cursor or your finger exactly at the menu. We can add a pseudo-element that is larger than the menu, and it will solve the problem.</p>
  637. <pre class="language-css"><code is:raw="" class="language-css"><span class="token selector">.menu:after</span> <span class="token punctuation">{</span>
  638. <span class="token property">content</span><span class="token punctuation">:</span> <span class="token string">""</span><span class="token punctuation">;</span>
  639. <span class="token property">position</span><span class="token punctuation">:</span> absolute<span class="token punctuation">;</span>
  640. <span class="token property">inset</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span>
  641. <span class="token property">z-index</span><span class="token punctuation">:</span> -1<span class="token punctuation">;</span>
  642. <span class="token property">transform</span><span class="token punctuation">:</span> <span class="token function">scale</span><span class="token punctuation">(</span>1.5<span class="token punctuation">)</span><span class="token punctuation">;</span>
  643. <span class="token punctuation">}</span></code></pre>
  644. <p>Easy and straightforward, right? Play with the demo below and see the difference.</p>
  645. <p><astro-island uid="ZOL472" prefix="r31" component-url="https://ishadeed.com/_astro/MobileMenu.jhXdaTWo.js" component-export="default" renderer-url="https://ishadeed.com/_astro/client.2wOxL1Aq.js" props='{"enhanced":[0,true]}' ssr="" client="visible" opts='{"name":"MobileMenu","value":true}' await-children=""></astro-island></p>
  646. <h4 id="modal-dismiss-button">Modal dismiss button</h4>
  647. <p>A model dismiss button is an important action that needs to be considered while building the UI.</p>
  648. <p>If the target size is too small, it will be hard to dismiss the modal, especially on touch devices.</p>
  649. <p><astro-island uid="12Mzfu" prefix="r32" component-url="https://ishadeed.com/_astro/ModalDismiss.F2g45n_x.js" component-export="default" renderer-url="https://ishadeed.com/_astro/client.2wOxL1Aq.js" props="{}" ssr="" client="visible" opts='{"name":"ModalDismiss","value":true}' await-children=""><div><div class="example-wrapper center _wrapper_1juvx_1"><div class="_modal_1juvx_7"><div class="_modalContent_1juvx_58"><p>title in here</p><p>a few description text just for the demo purpose.</p></div></div></div></div></astro-island></p>
  650. <p>What are the solutions that we can do?</p>
  651. <ul>
  652. <li>Increase the size of the dismiss button.</li>
  653. <li>Increase the target area only by adding a pseudo-element</li>
  654. </ul>
  655. <p><picture> <source srcset="https://ishadeed.com/_astro/modal-1.-bVW1gH8_ZqytJw.png" type="image/png"><source srcset="https://ishadeed.com/_astro/modal-1.-bVW1gH8_22t23P.webp" type="image/webp"> <img src="https://ishadeed.com/_astro/modal-1.-bVW1gH8_ZqytJw.png" alt="" loading="lazy" decoding="async"> </source></source></picture></p>
  656. <p>The first solution works, but it increases the size of the header, which
  657. isn’t needed. Also, designers won’t like that 😉.</p>
  658. <p>Instead, we can add a pseudo-element to the dismiss button and it will increase the size without affecting the modal header.</p>
  659. <pre class="language-css"><code is:raw="" class="language-css"><span class="token selector">.button:after</span> <span class="token punctuation">{</span>
  660. <span class="token property">content</span><span class="token punctuation">:</span> <span class="token string">""</span><span class="token punctuation">;</span>
  661. <span class="token property">position</span><span class="token punctuation">:</span> absolute<span class="token punctuation">;</span>
  662. <span class="token property">inset</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span>
  663. <span class="token property">z-index</span><span class="token punctuation">:</span> -1<span class="token punctuation">;</span>
  664. <span class="token property">transform</span><span class="token punctuation">:</span> <span class="token function">scale</span><span class="token punctuation">(</span>1.5<span class="token punctuation">)</span><span class="token punctuation">;</span>
  665. <span class="token punctuation">}</span></code></pre>
  666. <p><astro-island uid="Z1ibAYg" prefix="r33" component-url="https://ishadeed.com/_astro/ModalDismiss.F2g45n_x.js" component-export="default" renderer-url="https://ishadeed.com/_astro/client.2wOxL1Aq.js" props='{"enhanced":[0,true]}' ssr="" client="visible" opts='{"name":"ModalDismiss","value":true}' await-children=""><div><div class="example-wrapper center _wrapper_1juvx_1"><div class="_modal_1juvx_7"><div class="_modalContent_1juvx_58"><p>title in here</p><p>a few description text just for the demo purpose.</p></div></div><p class="checkbox"><input id=":r33R2:" type="checkbox" class="sr-only _input_3qp17_1"><label class="_label_3qp17_16" for=":r33R2:">Enhance target size</label></p></div></div></astro-island></p>
  667. <p>Fixed 🥳</p>
  668. <p>An example of having a large target size is a section header. There is a link that redirects the user to the full content.</p>
  669. <p>At first glance, the arrow looks fine. Try to hover on the arrow link. Notice how the target area is small.</p>
  670. <p><astro-island uid="1xYYb7" prefix="r34" component-url="https://ishadeed.com/_astro/SectionHeader.IaPXyJUt.js" component-export="default" renderer-url="https://ishadeed.com/_astro/client.2wOxL1Aq.js" props='{"variation":[0,"1"]}' ssr="" client="visible" opts='{"name":"SectionHeader","value":true}' await-children=""></astro-island></p>
  671. <p>The spacing around the arrow is padding for the section header.</p>
  672. <p><astro-island uid="LOF9P" prefix="r35" component-url="https://ishadeed.com/_astro/SectionHeader.IaPXyJUt.js" component-export="default" renderer-url="https://ishadeed.com/_astro/client.2wOxL1Aq.js" props='{"variation":[0,"2"]}' ssr="" client="visible" opts='{"name":"SectionHeader","value":true}' await-children=""></astro-island></p>
  673. <p>To fix that, we have two options:</p>
  674. <ul>
  675. <li>Remove padding from the header and add it to the arrow button.</li>
  676. <li>Keep the padding on the header and use a pseudo-element to increase the target size.</li>
  677. </ul>
  678. <p>I will opt-in for the pseudo solution. Feel free to do the solution that works best for your use-case.</p>
  679. <pre class="language-css"><code is:raw="" class="language-css"><span class="token selector">.section__more:after</span> <span class="token punctuation">{</span>
  680. <span class="token property">content</span><span class="token punctuation">:</span> <span class="token string">""</span><span class="token punctuation">;</span>
  681. <span class="token property">position</span><span class="token punctuation">:</span> absolute<span class="token punctuation">;</span>
  682. <span class="token property">inset</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span>
  683. <span class="token property">z-index</span><span class="token punctuation">:</span> -1<span class="token punctuation">;</span>
  684. <span class="token property">transform</span><span class="token punctuation">:</span> <span class="token function">scale</span><span class="token punctuation">(</span>1.5<span class="token punctuation">)</span><span class="token punctuation">;</span>
  685. <span class="token punctuation">}</span></code></pre>
  686. <p><astro-island uid="1JmVfE" prefix="r36" component-url="https://ishadeed.com/_astro/SectionHeader.IaPXyJUt.js" component-export="default" renderer-url="https://ishadeed.com/_astro/client.2wOxL1Aq.js" props='{"variation":[0,"2"],"toggleEnhance":[0,true]}' ssr="" client="visible" opts='{"name":"SectionHeader","value":true}' await-children=""></astro-island></p>
  687. <h4 id="search-component">Search component</h4>
  688. <p>Oftentimes, we add an icon to the search component. It’s recommended to give it a enough target size.</p>
  689. <p><astro-island uid="Xnm4j" prefix="r37" component-url="https://ishadeed.com/_astro/SearchFilter.DBhRpvXm.js" component-export="default" renderer-url="https://ishadeed.com/_astro/client.2wOxL1Aq.js" props='{"variation":[0,"1"]}' ssr="" client="visible" opts='{"name":"SearchFilter","value":true}' await-children=""></astro-island></p>
  690. <p>In the above example, the filter button size is constrained to the icon only. We can increase the size via padding, sizing the icon, or expanding the target area with an additional element.</p>
  691. <pre class="language-css"><code is:raw="" class="language-css"><span class="token selector">.search__filter:after</span> <span class="token punctuation">{</span>
  692. <span class="token property">content</span><span class="token punctuation">:</span> <span class="token string">""</span><span class="token punctuation">;</span>
  693. <span class="token property">position</span><span class="token punctuation">:</span> absolute<span class="token punctuation">;</span>
  694. <span class="token property">inset</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span>
  695. <span class="token property">z-index</span><span class="token punctuation">:</span> -1<span class="token punctuation">;</span>
  696. <span class="token property">transform</span><span class="token punctuation">:</span> <span class="token function">scale</span><span class="token punctuation">(</span>2<span class="token punctuation">)</span><span class="token punctuation">;</span>
  697. <span class="token punctuation">}</span></code></pre>
  698. <p><astro-island uid="Z6GnhQ" prefix="r38" component-url="https://ishadeed.com/_astro/SearchFilter.DBhRpvXm.js" component-export="default" renderer-url="https://ishadeed.com/_astro/client.2wOxL1Aq.js" props='{"variation":[0,"2"],"toggleEnhance":[0,true]}' ssr="" client="visible" opts='{"name":"SearchFilter","value":true}' await-children=""></astro-island></p>
  699. <p>Much better.</p>
  700. <p>In a profile menu toggle, having the action only on the arrow icon isn’t a good practice. It forces the user to laser-point their mouse to open the menu.</p>
  701. <p><astro-island uid="Z2i5n2K" prefix="r39" component-url="https://ishadeed.com/_astro/ProfileMenu.NcPq9QU7.js" component-export="default" renderer-url="https://ishadeed.com/_astro/client.2wOxL1Aq.js" props="{}" ssr="" client="visible" opts='{"name":"ProfileMenu","value":true}' await-children=""></astro-island></p>
  702. <p>Instead, the target size should be on the whole block (name, avatar, and icon). To do that, we can use a pseudo-element to extend the target size.</p>
  703. <pre class="language-css"><code is:raw="" class="language-css"><span class="token selector">.siteHeaderActions</span> <span class="token punctuation">{</span>
  704. <span class="token property">position</span><span class="token punctuation">:</span> relative<span class="token punctuation">;</span>
  705. <span class="token punctuation">}</span>
  706. <span class="token selector">.button:after</span> <span class="token punctuation">{</span>
  707. <span class="token property">position</span><span class="token punctuation">:</span> absolute<span class="token punctuation">;</span>
  708. <span class="token property">inset</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span>
  709. <span class="token punctuation">}</span></code></pre>
  710. <p><astro-island uid="219Gx6" prefix="r40" component-url="https://ishadeed.com/_astro/ProfileMenu.NcPq9QU7.js" component-export="default" renderer-url="https://ishadeed.com/_astro/client.2wOxL1Aq.js" props='{"toggleEnhance":[0,true]}' ssr="" client="visible" opts='{"name":"ProfileMenu","value":true}' await-children=""></astro-island></p>
  711. <p>Even better, we can add a background and make it more clear that all of
  712. the block is clickable.</p>
  713. <p><astro-island uid="ZTA6BG" prefix="r41" component-url="https://ishadeed.com/_astro/ProfileMenu.NcPq9QU7.js" component-export="default" renderer-url="https://ishadeed.com/_astro/client.2wOxL1Aq.js" props='{"toggleEnhance":[0,true],"boxed":[0,true]}' ssr="" client="visible" opts='{"name":"ProfileMenu","value":true}' await-children=""></astro-island></p>
  714. <h3 id="checkboxes-and-radio-buttons">Checkboxes and radio buttons</h3>
  715. <p>Let’s highlight something here. The default styling for checkboxes and radio buttons on the web isn’t accessible for touch users. I guess the reason is because these controls weren’t designed for touch in the first place.</p>
  716. <p><astro-island uid="Z1NAlBu" prefix="r42" component-url="https://ishadeed.com/_astro/CheckboxExample.8UrfXGPq.js" component-export="default" renderer-url="https://ishadeed.com/_astro/client.2wOxL1Aq.js" props='{"variation":[0,"1"]}' ssr="" client="visible" opts='{"name":"CheckboxExample","value":true}' await-children=""></astro-island></p>
  717. <p>I experimented to validate that. In the following photo, see how the tip of my thumb is almost the size of three checkboxes. It’s very hard to choose an option. This is the default style for checkboxes.</p>
  718. <p><picture> <source srcset="https://ishadeed.com/_astro/example-checkbox-1.QtNLxMH4_175es6.png" type="image/png"><source srcset="https://ishadeed.com/_astro/example-checkbox-1.QtNLxMH4_1DGMCK.webp" type="image/webp"> <img src="https://ishadeed.com/_astro/example-checkbox-1.QtNLxMH4_175es6.png" alt="" loading="lazy" decoding="async"> </source></source></picture></p>
  719. <p>Let’s explore how to make this much better.</p>
  720. <h4 id="link-the-input-with-the-label">Link the input with the label</h4>
  721. <p>Before diving into this solution, let’s highlight the issue it prevents.</p>
  722. <p>Say that we have the following HTML: an input and a label.</p>
  723. <pre class="language-html"><code is:raw="" class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>input</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">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>feedback<span class="token punctuation">"</span></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>feedback1<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
  724. <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>label</span><span class="token punctuation">&gt;</span></span>Social media<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>label</span><span class="token punctuation">&gt;</span></span></code></pre>
  725. <p>The target area will be only on the checkbox (the highlighted area).</p>
  726. <p><astro-island uid="TLr98" prefix="r43" component-url="https://ishadeed.com/_astro/CheckboxExample.8UrfXGPq.js" component-export="default" renderer-url="https://ishadeed.com/_astro/client.2wOxL1Aq.js" props='{"variation":[0,"1"]}' ssr="" client="visible" opts='{"name":"CheckboxExample","value":true}' await-children=""></astro-island></p>
  727. <p>To fix that, the input ID must be used in the <code>for</code> attribute of the label. Such a quick fix with a big impact on the user experience and accessibility.</p>
  728. <pre class="language-html"><code is:raw="" class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>input</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">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>feedback<span class="token punctuation">"</span></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>feedback1<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
  729. <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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>feedback1<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>Social media<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>label</span><span class="token punctuation">&gt;</span></span></code></pre>
  730. <p><astro-island uid="RIQtj" prefix="r44" component-url="https://ishadeed.com/_astro/CheckboxExample.8UrfXGPq.js" component-export="default" renderer-url="https://ishadeed.com/_astro/client.2wOxL1Aq.js" props='{"variation":[0,"2"]}' ssr="" client="visible" opts='{"name":"CheckboxExample","value":true}' await-children=""></astro-island></p>
  731. <h4 id="make-the-checkbox-bigger">Make the checkbox bigger</h4>
  732. <p>The next step is to increase the checkbox size. To make things easier, I will do a custom checkbox and hide the default one. This is better to have a consistent experience across different browsers and devices.</p>
  733. <ul>
  734. <li>Hide the default input. I used the <code>sr-only</code> solution to hide it only visually.</li>
  735. <li>Styled a custom element for the label.</li>
  736. </ul>
  737. <pre class="language-html"><code is:raw="" class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>input</span>
  738. <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>”sr-only”<span class="token punctuation">"</span></span>
  739. <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>
  740. <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>feedback<span class="token punctuation">"</span></span>
  741. <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>feedback1<span class="token punctuation">"</span></span>
  742. <span class="token punctuation">/&gt;</span></span>
  743. Social media
  744. <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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>feedback1<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>label</span><span class="token punctuation">&gt;</span></span></code></pre>
  745. <p><astro-island uid="2nCbld" prefix="r45" component-url="https://ishadeed.com/_astro/CheckboxExample.8UrfXGPq.js" component-export="default" renderer-url="https://ishadeed.com/_astro/client.2wOxL1Aq.js" props='{"variation":[0,"3"]}' ssr="" client="visible" opts='{"name":"CheckboxExample","value":true}' await-children=""></astro-island></p>
  746. <h4 id="add-padding">Add padding</h4>
  747. <p>Increasing the padding around the checkbox will make the target area even larger.</p>
  748. <pre class="language-css"><code is:raw="" class="language-css"><span class="token selector">label</span> <span class="token punctuation">{</span>
  749. <span class="token property">padding-block</span><span class="token punctuation">:</span> 3px<span class="token punctuation">;</span>
  750. <span class="token punctuation">}</span></code></pre>
  751. <p><astro-island uid="Z2cPodr" prefix="r46" component-url="https://ishadeed.com/_astro/CheckboxExample.8UrfXGPq.js" component-export="default" renderer-url="https://ishadeed.com/_astro/client.2wOxL1Aq.js" props='{"variation":[0,"4"]}' ssr="" client="visible" opts='{"name":"CheckboxExample","value":true}' await-children=""></astro-island></p>
  752. <h4 id="increase-spacing-between-each-row">Increase spacing between each row</h4>
  753. <p>You might be wondering, the article is about increasing the target size, but the checkboxes are too close to each other. Let’s fix that.</p>
  754. <pre class="language-css"><code is:raw="" class="language-css"><span class="token selector">.form-group</span> <span class="token punctuation">{</span>
  755. <span class="token property">gap</span><span class="token punctuation">:</span> 8px<span class="token punctuation">;</span>
  756. <span class="token punctuation">}</span></code></pre>
  757. <p><astro-island uid="s6o8o" prefix="r47" component-url="https://ishadeed.com/_astro/CheckboxExample.8UrfXGPq.js" component-export="default" renderer-url="https://ishadeed.com/_astro/client.2wOxL1Aq.js" props='{"variation":[0,"5"]}' ssr="" client="visible" opts='{"name":"CheckboxExample","value":true}' await-children=""></astro-island></p>
  758. <h4 id="make-the-labels-equal-to-the-largest-one">Make the labels equal to the largest one</h4>
  759. <p>Currently, the width of each option is equal to its container. This is a large target area that can get even larger if the container is taking the full width.</p>
  760. <p>See the following example.</p>
  761. <p><astro-island uid="Z1i3zVB" prefix="r48" component-url="https://ishadeed.com/_astro/CheckboxExample.8UrfXGPq.js" component-export="default" renderer-url="https://ishadeed.com/_astro/client.2wOxL1Aq.js" props='{"variation":[0,"6"]}' ssr="" client="visible" opts='{"name":"CheckboxExample","value":true}' await-children=""></astro-island></p>
  762. <p>This is a very large target area that isn’t needed in my opinion. What we can do instead is to limit the width of all options to the largest of them.</p>
  763. <p>We can get the benefit of <code>max-content</code> in CSS. When we add it to the options container, its width will be equal to the longest option.</p>
  764. <pre class="language-css"><code is:raw="" class="language-css"><span class="token selector">.form-group</span> <span class="token punctuation">{</span>
  765. <span class="token property">width</span><span class="token punctuation">:</span> max-content<span class="token punctuation">;</span>
  766. <span class="token punctuation">}</span></code></pre>
  767. <p><astro-island uid="ZBIeVa" prefix="r49" component-url="https://ishadeed.com/_astro/CheckboxExample.8UrfXGPq.js" component-export="default" renderer-url="https://ishadeed.com/_astro/client.2wOxL1Aq.js" props='{"variation":[0,"7"]}' ssr="" client="visible" opts='{"name":"CheckboxExample","value":true}' await-children=""></astro-island></p>
  768. <p>On mobile, we can increase the spacing between the checkbox items. We might not need that. Please make sure to test them.</p>
  769. <p>I made a final demo that show the process of enhancing the UI. Move the slider to see the gradual enhancement.</p>
  770. <p><astro-island uid="ZGMdKl" prefix="r50" component-url="https://ishadeed.com/_astro/DemoCheckbox.AEQa6zTC.js" component-export="default" renderer-url="https://ishadeed.com/_astro/client.2wOxL1Aq.js" props="{}" ssr="" client="visible" opts='{"name":"DemoCheckbox","value":true}' await-children=""></astro-island></p>
  771. <p>With that, here is the enhanced target size with an actual larger thumb using them (Hey there!).</p>
  772. <p><picture> <source srcset="https://ishadeed.com/_astro/example-checkbox-2.s6ysUW0v_1a10bj.png" type="image/png"><source srcset="https://ishadeed.com/_astro/example-checkbox-2.s6ysUW0v_1GCylX.webp" type="image/webp"> <img src="https://ishadeed.com/_astro/example-checkbox-2.s6ysUW0v_1a10bj.png" alt="" loading="lazy" decoding="async"> </source></source></picture></p>
  773. <p>From the photo, I can see that my thumb tip is being able to tap a specific
  774. checkbox without worrying about checking one by mistake.</p>
  775. <p>Since humans like visuals more, here is a side-by-side comparison of the before and after.</p>
  776. <p><picture> <source srcset="https://ishadeed.com/_astro/example-checkbox-3.VXbf8iIk_Z14ocTL.png" type="image/png"><source srcset="https://ishadeed.com/_astro/example-checkbox-3.VXbf8iIk_ZwLDJ7.webp" type="image/webp"> <img src="https://ishadeed.com/_astro/example-checkbox-3.VXbf8iIk_Z14ocTL.png" alt="" loading="lazy" decoding="async"> </source></source></picture></p>
  777. <p>Notice how the difference is clear. Always make sure to have a large target
  778. size. See the following video for me trying to use the checkboxes.</p>
  779. <video poster="https://ishadeed.com/assets/target-size/videos/test-poster.png" src="https://ishadeed.com/assets/target-size/videos/test-video.mp4" controls controlslist="nodownload"></video>
  780. <h3 id="action-buttons">Action buttons</h3>
  781. <p>Buttons are a foundational component of the web. On every website you visit, you might need to interact with a button. When built right, they can be effective and easy to use for the user.</p>
  782. <p>In the given example, two buttons are present. It’s possible to tap the wrong button due to their small size and minimal spacing.</p>
  783. <p>Try to change the thumb size and notice how the touch indicator can easily overlap the buttons at once. This isn’t good.</p>
  784. <p><astro-island uid="Z1IVVvi" prefix="r51" component-url="https://ishadeed.com/_astro/TouchIndicator.8MUI1jDX.js" component-export="default" renderer-url="https://ishadeed.com/_astro/client.2wOxL1Aq.js" props="{}" ssr="" client="visible" opts='{"name":"TouchIndicator","value":true}' await-children=""><div class="touch-test"><div class="box"><div class="content undefined"><h3>Please review changes</h3><p>All changes will be saved upon confirming.</p></div><p class="touch"></p></div></div></astro-island></p>
  785. <p>We have two options to fix that:</p>
  786. <ul>
  787. <li>Increase button size and spacing</li>
  788. <li>Display each button it a new line with full width</li>
  789. </ul>
  790. <p><picture> <source srcset="https://ishadeed.com/_astro/example-buttons-1._JwUmOce_1UYccD.png" type="image/png"><source srcset="https://ishadeed.com/_astro/example-buttons-1._JwUmOce_MYCQR.webp" type="image/webp"> <img src="https://ishadeed.com/_astro/example-buttons-1._JwUmOce_1UYccD.png" alt="" loading="lazy" decoding="async"> </source></source></picture>I won’t
  791. discuss which solution is the best, but the idea is that we want to
  792. increase the target size. In both solutions, it’s harder to overlap or
  793. tap a button my mistake.</p>
  794. <pre class="language-css"><code is:raw="" class="language-css"><span class="token selector">.button-group</span> <span class="token punctuation">{</span>
  795. <span class="token property">display</span><span class="token punctuation">:</span> flex<span class="token punctuation">;</span>
  796. <span class="token property">gap</span><span class="token punctuation">:</span> 0.5rem<span class="token punctuation">;</span>
  797. <span class="token punctuation">}</span>
  798. <span class="token selector">​ .button</span> <span class="token punctuation">{</span>
  799. <span class="token property">padding</span><span class="token punctuation">:</span> 0.5rem 1rem<span class="token punctuation">;</span>
  800. <span class="token punctuation">}</span></code></pre>
  801. <p>Or..</p>
  802. <pre class="language-css"><code is:raw="" class="language-css"><span class="token selector">.button-group</span> <span class="token punctuation">{</span>
  803. <span class="token property">display</span><span class="token punctuation">:</span> flex<span class="token punctuation">;</span>
  804. <span class="token property">flex-direction</span><span class="token punctuation">:</span> column<span class="token punctuation">;</span>
  805. <span class="token property">gap</span><span class="token punctuation">:</span> 0.5rem<span class="token punctuation">;</span>
  806. <span class="token punctuation">}</span>
  807. <span class="token selector">​ .button</span> <span class="token punctuation">{</span>
  808. <span class="token property">padding</span><span class="token punctuation">:</span> 0.5rem 1rem<span class="token punctuation">;</span>
  809. <span class="token punctuation">}</span></code></pre>
  810. <p>For the sake of this example, I will go with the first solution.</p>
  811. <p><astro-island uid="2en9q5" prefix="r52" component-url="https://ishadeed.com/_astro/TouchIndicator.8MUI1jDX.js" component-export="default" renderer-url="https://ishadeed.com/_astro/client.2wOxL1Aq.js" props='{"enhanced":[0,true]}' ssr="" client="visible" opts='{"name":"TouchIndicator","value":true}' await-children=""><div class="touch-test"><div class="box"><div class="content enhanced"><h3>Please review changes</h3><p>All changes will be saved upon confirming.</p></div><p class="touch"></p></div></div></astro-island></p>
  812. <h3 id="text-buttons">Text buttons</h3>
  813. <p>Text buttons are often used to give more focus to the surrounding content, or to use them as a secondary button.</p>
  814. <p>In the following demo, the text button “learn more” looks like it has a good target size. Toggle the outline and see it.</p>
  815. <p><astro-island uid="268TaJ" prefix="r53" component-url="https://ishadeed.com/_astro/TextButton.Gu2escNA.js" component-export="default" renderer-url="https://ishadeed.com/_astro/client.2wOxL1Aq.js" props='{"showOutline":[0,true]}' ssr="" client="visible" opts='{"name":"TextButton","value":true}' await-children=""><div><div class="example-wrapper center _wrapper_1hqq0_1"><article class="_card_1hqq0_7"><img src="https://ishadeed.com/assets/target-size/cookies.jpg" alt=""><div><h3>Yummy cookies</h3><p>Learn how to make great cookies at home.</p></div></article></div></div></astro-island></p>
  816. <p>The simplest fix is to add <code>padding</code> around the button itself.</p>
  817. <pre class="language-css"><code is:raw="" class="language-css"><span class="token selector">.button</span> <span class="token punctuation">{</span>
  818. <span class="token property">padding</span><span class="token punctuation">:</span> 8px<span class="token punctuation">;</span>
  819. <span class="token punctuation">}</span></code></pre>
  820. <p><astro-island uid="Z12Ob50" prefix="r54" component-url="https://ishadeed.com/_astro/TextButton.Gu2escNA.js" component-export="default" renderer-url="https://ishadeed.com/_astro/client.2wOxL1Aq.js" props='{"showOutline":[0,true],"enhanced":[0,true]}' ssr="" client="visible" opts='{"name":"TextButton","value":true}' await-children=""><div><div class="example-wrapper center _wrapper_1hqq0_1"><article class="_card_1hqq0_7"><img src="https://ishadeed.com/assets/target-size/cookies.jpg" alt=""><div><h3>Yummy cookies</h3><p>Learn how to make great cookies at home.</p></div></article></div></div></astro-island></p>
  821. <p>Better, right?</p>
  822. <p>Another example where text buttons can be used is in a group of buttons. Say we have a modal with a confirm and cancel button.</p>
  823. <p><astro-island uid="1iLYfO" prefix="r55" component-url="https://ishadeed.com/_astro/ConfirmSection.7blkEYyt.js" component-export="default" renderer-url="https://ishadeed.com/_astro/client.2wOxL1Aq.js" props='{"showOutline":[0,true]}' ssr="" client="visible" opts='{"name":"ConfirmSection","value":true}' await-children=""><div><div class="example-wrapper center _wrapper_1dai1_1"><article class="_card_1dai1_7"><div><div><h3>Are you sure?</h3><p>You are about to change nothing since this is just a UI example.</p></div></div></article></div></div></astro-island></p>
  824. <p>To fix that, we need to make sure that the text button has padding around it.</p>
  825. <p><astro-island uid="Z253uYi" prefix="r56" component-url="https://ishadeed.com/_astro/ConfirmSection.7blkEYyt.js" component-export="default" renderer-url="https://ishadeed.com/_astro/client.2wOxL1Aq.js" props='{"showOutline":[0,true],"enhanced":[0,true]}' ssr="" client="visible" opts='{"name":"ConfirmSection","value":true}' await-children=""><div><div class="example-wrapper center _wrapper_1dai1_1"><article class="_card_1dai1_7"><div><div><h3>Are you sure?</h3><p>You are about to change nothing since this is just a UI example.</p></div></div></article></div></div></astro-island></p>
  826. <p>The pagination is often used to organize large amounts of content so the user can access them easily.</p>
  827. <p>Let’s explore a pagination example that looks fine from a visual perspective, but raises important questions when it comes to the target area size.</p>
  828. <p><astro-island uid="Z1MgAO6" prefix="r57" component-url="https://ishadeed.com/_astro/Pagination.GQenUq0_.js" component-export="default" renderer-url="https://ishadeed.com/_astro/client.2wOxL1Aq.js" props='{"variation":[0,"1"]}' ssr="" client="visible" opts='{"name":"Pagination","value":true}' await-children=""></astro-island></p>
  829. <p>Notice the target area when it’s highlighted.</p>
  830. <ul>
  831. <li>Target size is small</li>
  832. <li>Spacing is large</li>
  833. </ul>
  834. <p>The spacing made look like the target size is large.</p>
  835. <p><astro-island uid="6P1w3" prefix="r58" component-url="https://ishadeed.com/_astro/Pagination.GQenUq0_.js" component-export="default" renderer-url="https://ishadeed.com/_astro/client.2wOxL1Aq.js" props='{"variation":[0,"2"],"spacingIndicator":[0,true]}' ssr="" client="visible" opts='{"name":"Pagination","value":true}' await-children=""></astro-island></p>
  836. <p>To fix that, we need to:</p>
  837. <ul>
  838. <li>Reduce the spacing</li>
  839. <li>Add more horizontal (inline) padding to each pagination item</li>
  840. </ul>
  841. <pre class="language-css"><code is:raw="" class="language-css"><span class="token selector">.pagination</span> <span class="token punctuation">{</span>
  842. <span class="token property">display</span><span class="token punctuation">:</span> flex<span class="token punctuation">;</span>
  843. <span class="token property">gap</span><span class="token punctuation">:</span> 4px<span class="token punctuation">;</span>
  844. <span class="token punctuation">}</span>
  845. <span class="token selector">.pagination__item</span> <span class="token punctuation">{</span>
  846. <span class="token property">padding</span><span class="token punctuation">:</span> 0.5rem<span class="token punctuation">;</span>
  847. <span class="token punctuation">}</span></code></pre>
  848. <p><astro-island uid="ZqBHF2" prefix="r59" component-url="https://ishadeed.com/_astro/Pagination.GQenUq0_.js" component-export="default" renderer-url="https://ishadeed.com/_astro/client.2wOxL1Aq.js" props='{"variation":[0,"3"],"toggleEnhance":[0,true]}' ssr="" client="visible" opts='{"name":"Pagination","value":true}' await-children=""></astro-island></p>
  849. <h3 id="vertical-navigation">Vertical navigation</h3>
  850. <p>In a previous Twitter design, the navigation was as follows. Each nav item size depends on its content.</p>
  851. <p><astro-island uid="UgDd3" prefix="r60" component-url="https://ishadeed.com/_astro/VerticalNav.rwmZXwOt.js" component-export="default" renderer-url="https://ishadeed.com/_astro/client.2wOxL1Aq.js" props='{"variation":[0,"1"],"Indicator":[0,"Non-clickable area"]}' ssr="" client="visible" opts='{"name":"VerticalNav$1","value":true}' await-children=""></astro-island></p>
  852. <p>This is a confusing behavior as the user might expect the full navigation element to be clickable.</p>
  853. <p><picture> <source srcset="https://ishadeed.com/_astro/vertical-nav-1.enZO-EDj_13yyaV.png" type="image/png"><source srcset="https://ishadeed.com/_astro/vertical-nav-1.enZO-EDj_Z1rOaiu.webp" type="image/webp"> <img src="https://ishadeed.com/_astro/vertical-nav-1.enZO-EDj_13yyaV.png" alt="" loading="lazy" decoding="async"> </source></source></picture></p>
  854. <p>Here is a version with each navigation item taking the full width. Much
  855. better.</p>
  856. <p><astro-island uid="R3q9F" prefix="r61" component-url="https://ishadeed.com/_astro/VerticalNav.rwmZXwOt.js" component-export="default" renderer-url="https://ishadeed.com/_astro/client.2wOxL1Aq.js" props='{"variation":[0,"1"],"toggleEnhance":[0,true],"helper":[0,"Toggle the checkbox to see the larger targets"]}' ssr="" client="visible" opts='{"name":"VerticalNav$1","value":true}' await-children=""><div><p>Toggle the checkbox to see the larger targets</p></div></astro-island></p>
  857. <h3 id="category-list">Category list</h3>
  858. <p>In the following example, the target size is on the label only. The spacing around the labels is padding added to the <code>&lt;li&gt;</code> element, not the <code>&lt;a&gt;</code> element.</p>
  859. <p><picture> <source srcset="https://ishadeed.com/_astro/category-list-1.84dVSplc_P5nbq.png" type="image/png"><source srcset="https://ishadeed.com/_astro/category-list-1.84dVSplc_ZOY3tL.webp" type="image/webp"> <img src="https://ishadeed.com/_astro/category-list-1.84dVSplc_P5nbq.png" alt="" loading="lazy" decoding="async"> </source></source></picture></p>
  860. <p>To fix that:</p>
  861. <ul>
  862. <li>Move the icon inside the <code>&lt;a&gt;</code> element.</li>
  863. <li>We need to remove the padding from the <code>&lt;li&gt;</code> and add it to the <code>&lt;a&gt;</code> element, and force them to take the full width.</li>
  864. </ul>
  865. <pre class="language-css"><code is:raw="" class="language-css"><span class="token selector">.nav-item a</span> <span class="token punctuation">{</span>
  866. <span class="token property">padding</span><span class="token punctuation">:</span> 0.8rem 1rem<span class="token punctuation">;</span>
  867. <span class="token property">display</span><span class="token punctuation">:</span> flex<span class="token punctuation">;</span>
  868. <span class="token punctuation">}</span></code></pre>
  869. <p><astro-island uid="ZyUqp1" prefix="r62" component-url="https://ishadeed.com/_astro/CategoryList.AMHssFfm.js" component-export="default" renderer-url="https://ishadeed.com/_astro/client.2wOxL1Aq.js" props='{"helper":[0,"Toggle the checkbox. This demo is using a pseudo-element for interactivity purposes."],"toggleEnhance":[0,true]}' ssr="" client="visible" opts='{"name":"VerticalNav","value":true}' await-children=""><div><p>Toggle the checkbox. This demo is using a pseudo-element for interactivity purposes.</p></div></astro-island></p>
  870. <p>On mobile, having a great scrolling experience is crucial to let the user explore more content. The other day, I was browsing Amazon on my mobile and noticed an interesting behavior.</p>
  871. <p>In the following figure, the scrollable section “You might also like” isn’t only on the cards, but also on the highlighted area underneath.</p>
  872. <p><picture> <source srcset="https://ishadeed.com/_astro/scrolling-container-1.j0KqkTzR_oi5Tc.png" type="image/png"><source srcset="https://ishadeed.com/_astro/scrolling-container-1.j0KqkTzR_1nhgd9.webp" type="image/webp"> <img src="https://ishadeed.com/_astro/scrolling-container-1.j0KqkTzR_oi5Tc.png" alt="" loading="lazy" decoding="async"> </source></source></picture></p>
  873. <p>In CSS, we can do such a thing by extending the bottom spacing. If we’re
  874. using CSS Scroll Snap, we need to add <code>padding-bottom</code> only.</p>
  875. <p>See the following demo.</p>
  876. <p><astro-island uid="QnnGW" prefix="r63" component-url="https://ishadeed.com/_astro/ScrollingContainer.J0ZtNdDg.js" component-export="default" renderer-url="https://ishadeed.com/_astro/client.2wOxL1Aq.js" props="{}" ssr="" client="visible" opts='{"name":"ScrollingContainer","value":true}' await-children=""><div><div class="example-wrapper center _wrapper_8ctok_1 undefined"><div><h3>You might also like</h3><section class="_section_8ctok_12 false"><div class="_sectionItem_8ctok_31"><p class="_sectionItemThumb_8ctok_47"></p><div><h4 class="_title_8ctok_39">Chocolate Cake</h4><p class="_desc_8ctok_43">Rich and moist chocolate cake with a velvety smooth ganache.</p></div></div><div class="_sectionItem_8ctok_31"><p class="_sectionItemThumb_8ctok_47"></p><div><h4 class="_title_8ctok_39">Strawberry Cheesecake</h4><p class="_desc_8ctok_43">Creamy cheesecake topped with fresh strawberries and a sweet glaze.</p></div></div><div class="_sectionItem_8ctok_31"><p class="_sectionItemThumb_8ctok_47"></p><div><h4 class="_title_8ctok_39">Apple Pie</h4><p class="_desc_8ctok_43">Flaky crust filled with cinnamon-spiced apples, baked to golden perfection.</p></div></div><div class="_sectionItem_8ctok_31"><p class="_sectionItemThumb_8ctok_47"></p><div><h4 class="_title_8ctok_39">Tiramisu</h4><p class="_desc_8ctok_43">Classic Italian dessert with layers of coffee-soaked ladyfingers and mascarpone cream.</p></div></div><div class="_sectionItem_8ctok_31"><p class="_sectionItemThumb_8ctok_47"></p><div><h4 class="_title_8ctok_39">Mango Sorbet</h4><p class="_desc_8ctok_43">Refreshing sorbet made with ripe mangoes, perfect for a tropical treat.</p></div></div></section></div><div><h3>Another section in here</h3><p>This is a random desc in here, just to show that there is another section under the scrollable one.</p></div></div></div></astro-island></p>
  877. <p>Try to scroll the section on mobile, and you will notice that the scrollable area is within the content only.</p>
  878. <p>We can extend that by increasing the bottom padding.</p>
  879. <pre class="language-css"><code is:raw="" class="language-css"><span class="token selector">.section</span> <span class="token punctuation">{</span>
  880. <span class="token property">padding-bottom</span><span class="token punctuation">:</span> 4rem<span class="token punctuation">;</span>
  881. <span class="token punctuation">}</span></code></pre>
  882. <p><astro-island uid="aC2t" prefix="r64" component-url="https://ishadeed.com/_astro/ScrollingContainer.J0ZtNdDg.js" component-export="default" renderer-url="https://ishadeed.com/_astro/client.2wOxL1Aq.js" props='{"helper":[0,"Try to scroll the container on mobile."],"toggleEnhance":[0,true]}' ssr="" client="visible" opts='{"name":"ScrollingContainer","value":true}' await-children=""><div><div class="example-wrapper center _wrapper_8ctok_1 _enhanced_8ctok_8"><div><h3>You might also like</h3><section class="_section_8ctok_12 false"><div class="_sectionItem_8ctok_31"><p class="_sectionItemThumb_8ctok_47"></p><div><h4 class="_title_8ctok_39">Chocolate Cake</h4><p class="_desc_8ctok_43">Rich and moist chocolate cake with a velvety smooth ganache.</p></div></div><div class="_sectionItem_8ctok_31"><p class="_sectionItemThumb_8ctok_47"></p><div><h4 class="_title_8ctok_39">Strawberry Cheesecake</h4><p class="_desc_8ctok_43">Creamy cheesecake topped with fresh strawberries and a sweet glaze.</p></div></div><div class="_sectionItem_8ctok_31"><p class="_sectionItemThumb_8ctok_47"></p><div><h4 class="_title_8ctok_39">Apple Pie</h4><p class="_desc_8ctok_43">Flaky crust filled with cinnamon-spiced apples, baked to golden perfection.</p></div></div><div class="_sectionItem_8ctok_31"><p class="_sectionItemThumb_8ctok_47"></p><div><h4 class="_title_8ctok_39">Tiramisu</h4><p class="_desc_8ctok_43">Classic Italian dessert with layers of coffee-soaked ladyfingers and mascarpone cream.</p></div></div><div class="_sectionItem_8ctok_31"><p class="_sectionItemThumb_8ctok_47"></p><div><h4 class="_title_8ctok_39">Mango Sorbet</h4><p class="_desc_8ctok_43">Refreshing sorbet made with ripe mangoes, perfect for a tropical treat.</p></div></div></section></div><div><h3>Another section in here</h3><p>This is a random desc in here, just to show that there is another section under the scrollable one.</p></div></div><p>Try to scroll the container on mobile.</p></div></astro-island></p>
  883. <p>This is better. Some may say there’s a big spacing under the section, and I agree. However, sometimes it’s okay because the benefits outweigh the drawbacks.</p>
  884. <h3 id="placement-of-target-items">Placement of target items</h3>
  885. <p>In cases where there is an action item at the top and a result at the bottom, the user’s finger on mobile might cover the result, making it annoying to see the change when for example, changing tabs, or toggling an option from a list.</p>
  886. <p>This example is from this article.</p>
  887. <p><astro-island uid="ZCkO4u" prefix="r65" component-url="https://ishadeed.com/_astro/DemoContext.riEHUkJo.js" component-export="default" renderer-url="https://ishadeed.com/_astro/client.2wOxL1Aq.js" props='{"variation":[0,"2"]}' ssr="" client="visible" opts='{"name":"DemoContext","value":true}' await-children=""><div><div class="example-wrapper center"><div class="demo-context"><div><svg viewbox="0 0 28 29" fill="none" xmlns="http://www.w3.org/2000/svg"><g opacity="0.5"><path d="M12.3484 12.808C12.3484 14.675 15.1484 14.675 15.1484 12.808C15.1484 10.941 12.3484 10.941 12.3484 12.808Z" fill="black"></path><path d="M17.8914 12.808C17.8914 14.675 20.6914 14.675 20.6914 12.808C20.6914 10.941 17.8914 10.941 17.8914 12.808Z" fill="black"></path><path d="M6.94414 12.808C6.94414 14.675 9.74414 14.675 9.74414 12.808C9.74414 10.941 6.94414 10.941 6.94414 12.808Z" fill="black"></path><path d="M9.51938 3.20386H18.5079C23.6037 3.20386 27.7754 7.34814 27.7754 12.4724C27.7754 17.5967 23.6311 21.741 18.5068 21.741H16.1826L9.26688 26.8653L9.26797 21.7127C4.28369 21.5727 0.252249 17.4841 0.252249 12.4441C0.251156 7.34842 4.42369 3.20414 9.51969 3.20414L9.51938 3.20386ZM9.51938 20.4239H10.5836V24.2596L15.735 20.4239H18.5066C22.9023 20.4239 26.458 16.8681 26.458 12.4724C26.458 8.0767 22.9023 4.52098 18.5066 4.52098L9.51916 4.51988C5.12344 4.51988 1.56771 8.0756 1.56771 12.4713C1.56771 16.8682 5.12366 20.4239 9.51938 20.4239Z" fill="black"></path></g></svg><p>I checked the order page on my <span>phone</span> and tried to <span>tap</span> the order button but it’s not responding.</p></div></div></div></div></astro-island></p>
  888. <p>At first glance, the positioning of the radio buttons might seem okay. On mobile, it’s something different.</p>
  889. <p><picture> <source srcset="https://ishadeed.com/_astro/placement-target-1.3wmZOrJa_Z1XLOjh.png" type="image/png"><source srcset="https://ishadeed.com/_astro/placement-target-1.3wmZOrJa_Z1rag8C.webp" type="image/webp"> <img src="https://ishadeed.com/_astro/placement-target-1.3wmZOrJa_Z1XLOjh.png" alt="" loading="lazy" decoding="async"> </source></source></picture></p>
  890. <p>Notice how my thumb is covering the result. It’s hard to see the changes
  891. while my thumb is choosing an option.</p>
  892. <p>The fix is simple. The placement of the options should be at the bottom.</p>
  893. <p><astro-island uid="vmLPo" prefix="r66" component-url="https://ishadeed.com/_astro/DemoContext.riEHUkJo.js" component-export="default" renderer-url="https://ishadeed.com/_astro/client.2wOxL1Aq.js" props='{"variation":[0,"1"]}' ssr="" client="visible" opts='{"name":"DemoContext","value":true}' await-children=""><div><div class="example-wrapper center"><div class="demo-context"><div><svg viewbox="0 0 28 29" fill="none" xmlns="http://www.w3.org/2000/svg"><g opacity="0.5"><path d="M12.3484 12.808C12.3484 14.675 15.1484 14.675 15.1484 12.808C15.1484 10.941 12.3484 10.941 12.3484 12.808Z" fill="black"></path><path d="M17.8914 12.808C17.8914 14.675 20.6914 14.675 20.6914 12.808C20.6914 10.941 17.8914 10.941 17.8914 12.808Z" fill="black"></path><path d="M6.94414 12.808C6.94414 14.675 9.74414 14.675 9.74414 12.808C9.74414 10.941 6.94414 10.941 6.94414 12.808Z" fill="black"></path><path d="M9.51938 3.20386H18.5079C23.6037 3.20386 27.7754 7.34814 27.7754 12.4724C27.7754 17.5967 23.6311 21.741 18.5068 21.741H16.1826L9.26688 26.8653L9.26797 21.7127C4.28369 21.5727 0.252249 17.4841 0.252249 12.4441C0.251156 7.34842 4.42369 3.20414 9.51969 3.20414L9.51938 3.20386ZM9.51938 20.4239H10.5836V24.2596L15.735 20.4239H18.5066C22.9023 20.4239 26.458 16.8681 26.458 12.4724C26.458 8.0767 22.9023 4.52098 18.5066 4.52098L9.51916 4.51988C5.12344 4.51988 1.56771 8.0756 1.56771 12.4713C1.56771 16.8682 5.12366 20.4239 9.51938 20.4239Z" fill="black"></path></g></svg><p>I checked the order page on my <span>phone</span> and tried to <span>tap</span> the order button but it’s not responding.</p></div></div></div></div></astro-island>
  894. <picture> <source srcset="https://ishadeed.com/_astro/placement-target-2.LwjLzU3l_1UBqaJ.png" type="image/png"><source srcset="https://ishadeed.com/_astro/placement-target-2.LwjLzU3l_2sdYlo.webp" type="image/webp"> <img src="https://ishadeed.com/_astro/placement-target-2.LwjLzU3l_1UBqaJ.png" alt="" loading="lazy" decoding="async"> </source></source></picture></p>
  895. <p>A real-life example of this is a UX detail I like on TikTok. When you tab the
  896. progress bar of the video, the timing will be shown above in a large
  897. size.</p>
  898. <p>This is a great UX detail, as it doesn’t show the timing under my finger or next to it. See the following figure:</p>
  899. <p><picture> <source srcset="https://ishadeed.com/_astro/placement-target-vs-result.j7Un2NEp_Z2dUT9I.png" type="image/png"><source srcset="https://ishadeed.com/_astro/placement-target-vs-result.j7Un2NEp_1qGa52.webp" type="image/webp"> <img src="https://ishadeed.com/_astro/placement-target-vs-result.j7Un2NEp_Z2dUT9I.png" alt="" loading="lazy" decoding="async"> </source></source></picture></p>
  900. <p>If the time were displayed on the progress bar itself, it would be harder
  901. for the user to spot it. Placing it at the top is better.</p>
  902. <p>On Instagram, it’s similar, but they also add a preview of the video.</p>
  903. <p><picture> <source srcset="https://ishadeed.com/_astro/placement-target-vs-result-2.SK-0Rka5_ZAWA4X.png" type="image/png"><source srcset="https://ishadeed.com/_astro/placement-target-vs-result-2.SK-0Rka5_Z1FaVUP.webp" type="image/webp"> <img src="https://ishadeed.com/_astro/placement-target-vs-result-2.SK-0Rka5_ZAWA4X.png" alt="" loading="lazy" decoding="async"> </source></source></picture></p>
  904. <h3 id="dominant-hand-of-the-user">Dominant hand of the user</h3>
  905. <p>Making it easy for the user to tap a target is not only about its size, but also it’s location. The following is an experiment based on a React hook by <a href="https://github.com/KittyGiraudel/dhand">Kitty Giraudel</a> called “dhand”.</p>
  906. <p>The idea is to guess what’s the dominant hand for the user and update the UI based on it.</p>
  907. <p><picture> <source srcset="https://ishadeed.com/_astro/dominant-hand-1.wzgmkaOW_hGm1B.png" type="image/png"><source srcset="https://ishadeed.com/_astro/dominant-hand-1.wzgmkaOW_Z1nn4DA.webp" type="image/webp"> <img src="https://ishadeed.com/_astro/dominant-hand-1.wzgmkaOW_hGm1B.png" alt="" loading="lazy" decoding="async"> </source></source></picture></p>
  908. <p>The assumption that this experience is based on is that a left-handed user
  909. will more likely tap on the far right of the UI. On the other hand, a right-handed
  910. will tap on the left side.</p>
  911. <p><picture> <source srcset="https://ishadeed.com/_astro/dominant-hand-2.dp3ednDd_ZdLRWK.png" type="image/png"><source srcset="https://ishadeed.com/_astro/dominant-hand-2.dp3ednDd_Z1SQjCW.webp" type="image/webp"> <img src="https://ishadeed.com/_astro/dominant-hand-2.dp3ednDd_ZdLRWK.png" alt="" loading="lazy" decoding="async"> </source></source></picture></p>
  912. <p>With that info in mind, we can make decisions about what’s the dominant
  913. hand. It’s an interesting experience. If I can learn one thing from this,
  914. it’s that target area isn’t only about size, but also the location.</p>
  915. <h2 id="testing-target-sizes">Testing target sizes</h2>
  916. <p>Spending time on a UI without testing it is a waste of time and effort. We should test the target sizes early on and keep an eye on them throughout the product building.</p>
  917. <p>To achieve that, we need to do testing in both the design and development. Let’s start with the design aspect first.</p>
  918. <h3 id="for-designers-delivering-a-clear-target-size-spec">For designers: delivering a clear target size spec</h3>
  919. <p>As a designer, it’s important to have clear documentation or a spec that explains the target size throughout the system.</p>
  920. <p>While this might not be needed for elements like buttons, it’s important for stuff like icon buttons, cards, tabs, mobile menus, you name it!</p>
  921. <h4 id="tabs-component">Tabs component</h4>
  922. <p>In a tabs component, the spacing should be around the tab item itself, not built from the outer padding of the parent.</p>
  923. <p><picture> <source srcset="https://ishadeed.com/_astro/test-designers-1.pFkXV2JK_2iawU4.png" type="image/png"><source srcset="https://ishadeed.com/_astro/test-designers-1.pFkXV2JK_1gTqaj.webp" type="image/webp"> <img src="https://ishadeed.com/_astro/test-designers-1.pFkXV2JK_2iawU4.png" alt="" loading="lazy" decoding="async"> </source></source></picture></p>
  924. <h4 id="slider-navigation">Slider navigation</h4>
  925. <p>This is a great example of keeping the UI as is, but expanding the target size to make it easier for the user to control the slider.</p>
  926. <p><picture> <source srcset="https://ishadeed.com/_astro/test-designers-2.j5oDanki_1uKRsc.png" type="image/png"><source srcset="https://ishadeed.com/_astro/test-designers-2.j5oDanki_mLj7q.webp" type="image/webp"> <img src="https://ishadeed.com/_astro/test-designers-2.j5oDanki_1uKRsc.png" alt="" loading="lazy" decoding="async"> </source></source></picture></p>
  927. <p>As a designer, thinking about those UI decisions upfront is important to
  928. deliver a great UX.</p>
  929. <h4 id="icon-buttons-1">Icon buttons</h4>
  930. <p>This applies to different cases and examples. Here is one for a modal dismiss button.</p>
  931. <p><picture> <source srcset="https://ishadeed.com/_astro/test-designers-3._YGew-vA_FfQCk.png" type="image/png"><source srcset="https://ishadeed.com/_astro/test-designers-3._YGew-vA_Zl0f7q.webp" type="image/webp"> <img src="https://ishadeed.com/_astro/test-designers-3._YGew-vA_FfQCk.png" alt="" loading="lazy" decoding="async"> </source></source></picture></p>
  932. <p>And this is for a close pattern that is placed at the top-right corner.
  933. Generally speaking, this pattern isn’t recommended.</p>
  934. <p>I spotted this in the tags menu in Threads by Meta.</p>
  935. <p><picture> <source srcset="https://ishadeed.com/_astro/test-designers-4.3Jkk6XZv_Z1DDesl.png" type="image/png"><source srcset="https://ishadeed.com/_astro/test-designers-4.3Jkk6XZv_2phMAP.webp" type="image/webp"> <img src="https://ishadeed.com/_astro/test-designers-4.3Jkk6XZv_Z1DDesl.png" alt="" loading="lazy" decoding="async"> </source></source></picture></p>
  936. <p>Here is my recommendation.</p>
  937. <p><picture> <source srcset="https://ishadeed.com/_astro/test-designers-5.KR37WxIr_10hWCt.png" type="image/png"><source srcset="https://ishadeed.com/_astro/test-designers-5.KR37WxIr_ZX97h.webp" type="image/webp"> <img src="https://ishadeed.com/_astro/test-designers-5.KR37WxIr_10hWCt.png" alt="" loading="lazy" decoding="async"> </source></source></picture></p>
  938. <h4 id="website-navigation-1">Website navigation</h4>
  939. <p>In a design tool like Figma, we sometimes forget the touch size details when we quickly mock up a UI. In this example, it’s a website header.</p>
  940. <p><picture> <source srcset="https://ishadeed.com/_astro/test-designers-6.Vajf4ul-_Z29J3LM.png" type="image/png"><source srcset="https://ishadeed.com/_astro/test-designers-6.Vajf4ul-_1fnDlW.webp" type="image/webp"> <img src="https://ishadeed.com/_astro/test-designers-6.Vajf4ul-_Z29J3LM.png" alt="" loading="lazy" decoding="async"> </source></source></picture></p>
  941. <p>In this design, the navigation items target size is only on the text. This
  942. isn’t good. Don’t make the developer think twice like:</p>
  943. <ul>
  944. <li>Did the designer mean to have them like this?</li>
  945. <li>What if the user clicked <code>3px</code> above a navigation item?</li>
  946. </ul>
  947. <p>An experienced developer will fix that themselves, or ask the designer first. But we can’t assume that all developers are aware of this as people have different levels of knowledge, which is fine.</p>
  948. <p><picture> <source srcset="https://ishadeed.com/_astro/test-designers-7.osLbV_Y9_1sfSGD.png" type="image/png"><source srcset="https://ishadeed.com/_astro/test-designers-7.osLbV_Y9_qYLVS.webp" type="image/webp"> <img src="https://ishadeed.com/_astro/test-designers-7.osLbV_Y9_1sfSGD.png" alt="" loading="lazy" decoding="async"> </source></source></picture></p>
  949. <h4 id="provide-a-target-size-component">Provide a target size component</h4>
  950. <p>Let’s suppose that you worked on a modal design and used an icon for a close button.</p>
  951. <p><picture> <source srcset="https://ishadeed.com/_astro/target-size-component-1.VM9rVhCi_Z2iFmYe.png" type="image/png"><source srcset="https://ishadeed.com/_astro/target-size-component-1.VM9rVhCi_Z2ki0gm.webp" type="image/webp"> <img src="https://ishadeed.com/_astro/target-size-component-1.VM9rVhCi_Z2iFmYe.png" alt="" loading="lazy" decoding="async"> </source></source></picture></p>
  952. <p>In Figma, the close icon doesn’t have any boundaries other than the icon
  953. itself, thus indicating to the developer that it should be that way.</p>
  954. <p>In the following example, I placed the icon within a Target component. I can use this component anywhere in the design.</p>
  955. <p><picture> <source srcset="https://ishadeed.com/_astro/target-size-component-2.BK1i_Aqk_y6EA1.png" type="image/png"><source srcset="https://ishadeed.com/_astro/target-size-component-2.BK1i_Aqk_wu2iS.webp" type="image/webp"> <img src="https://ishadeed.com/_astro/target-size-component-2.BK1i_Aqk_y6EA1.png" alt="" loading="lazy" decoding="async"> </source></source></picture></p>
  956. <p>Making this clear in the design is important for both the designer and
  957. the developer. If they know it, it’s a friendly reminder. If not, then
  958. they’ve learned something new!</p>
  959. <h3 id="for-developers-testing-target-sizes">For developers: testing target sizes</h3>
  960. <p>Once the design we have is well documented for target size rules, it’s time to test the UI for any small target sizes.</p>
  961. <p>The browser DevTools is a powerful way to detect and spot target size issues. The simplest thing you can do is to check the width of a target size by inspecting the element.</p>
  962. <p><picture> <source srcset="https://ishadeed.com/_astro/developers-test-1.iJ5AJveK_2lN9xh.png" type="image/png"><source srcset="https://ishadeed.com/_astro/developers-test-1.iJ5AJveK_1dNAcv.webp" type="image/webp"> <img src="https://ishadeed.com/_astro/developers-test-1.iJ5AJveK_2lN9xh.png" alt="" loading="lazy" decoding="async"> </source></source></picture></p>
  963. <p>This is good for testing a simple UI, but for a UI with lots of details,
  964. you will need to automate that somehow.</p>
  965. <h4 id="use-css-outline-to-have-a-look-at-the-target-size">Use CSS outline to have a look at the target size</h4>
  966. <p>Applying a CSS outline to every link and button on the page is a good mechanism. It helps you to see the outer box for every interactive element on the page.</p>
  967. <p>I worked on a proof of concept snippet that adds an outline to every button and link on the page plus showing the size of each.</p>
  968. <p>See it in the demo below:</p>
  969. <p class="codepen" data-height="504.97265625" data-theme-id="light" data-slug-hash="wvOWLQw" data-user="shadeed"></p>
  970. <p><a href="https://polypane.app/">Polypane browser</a> by Kilian Valkhof is a great browser for testing and debugging websites. There is a “Target size” debug feature that lets you pick a size for the cursor.</p>
  971. <p><picture> <source srcset="https://ishadeed.com/_astro/developers-test-3.AfckxL0L_ZWiBSm.png" type="image/png"><source srcset="https://ishadeed.com/_astro/developers-test-3.AfckxL0L_Z25ibe8.webp" type="image/webp"> <img src="https://ishadeed.com/_astro/developers-test-3.AfckxL0L_ZWiBSm.png" alt="" loading="lazy" decoding="async"> </source></source></picture></p>
  972. <p>Once you pick the target size (48, 44, or 24), the cursor will become the
  973. size of the selected option. It’s useful to spot UI elements that will
  974. interfere with each other.</p>
  975. <p>Here is an example in testing my headers-css project, the search and menu buttons should have more spacing between each other.</p>
  976. <p><picture> <source srcset="https://ishadeed.com/_astro/developers-test-4.jiYYm-Pw_ZrpaQ1.png" type="image/png"><source srcset="https://ishadeed.com/_astro/developers-test-4.jiYYm-Pw_Z1zoJbM.webp" type="image/webp"> <img src="https://ishadeed.com/_astro/developers-test-4.jiYYm-Pw_ZrpaQ1.png" alt="" loading="lazy" decoding="async"> </source></source></picture></p>
  977. <p>Another detail that I liked in Polypane is highlighting small target areas
  978. and how they interfere with each other. Here is an example of the default
  979. HTML checkboxes:</p>
  980. <p><picture> <source srcset="https://ishadeed.com/_astro/developers-test-5.EVJscJ5s_ZCyg9D.png" type="image/png"><source srcset="https://ishadeed.com/_astro/developers-test-5.EVJscJ5s_Z1KxOup.webp" type="image/webp"> <img src="https://ishadeed.com/_astro/developers-test-5.EVJscJ5s_ZCyg9D.png" alt="" loading="lazy" decoding="async"> </source></source></picture></p>
  981. <h2 id="real-life-examples">Real-life examples</h2>
  982. <p>You can’t make an article on a UX topic without showcasing a practical example. Let’s explore examples that I spotted on the web and how to fix them.</p>
  983. <p>The other day, I was listenting to a Twitter space, and I was about to leave the space by mistake because the “Leave” button is overlapping with the context menu (AKA the dots icon).</p>
  984. <p><astro-island uid="ZDDoJP" prefix="r67" component-url="https://ishadeed.com/_astro/TwitterSpaces.U4-9hhPt.js" component-export="default" renderer-url="https://ishadeed.com/_astro/client.2wOxL1Aq.js" props='{"helper":[0,"Try to hover on the Leave button or the dots menu."],"variation":[0,"1"]}' ssr="" client="visible" opts='{"name":"TwitterSpaces","value":true}' await-children=""><div><p>Try to hover on the Leave button or the dots menu.</p></div></astro-island></p>
  985. <p>Did you notice that the dots and “leave” buttons overlap? Let me make it more clear.</p>
  986. <p><astro-island uid="2cur99" prefix="r68" component-url="https://ishadeed.com/_astro/TwitterSpaces.U4-9hhPt.js" component-export="default" renderer-url="https://ishadeed.com/_astro/client.2wOxL1Aq.js" props='{"variation":[0,"2"]}' ssr="" client="visible" opts='{"name":"TwitterSpaces","value":true}' await-children=""></astro-island></p>
  987. <p>This isn’t good. To fix it, we simply need to add spacing between the icons buttons and the “leave” button.</p>
  988. <pre class="language-css"><code is:raw="" class="language-css"><span class="token selector">.header</span> <span class="token punctuation">{</span>
  989. <span class="token property">gap</span><span class="token punctuation">:</span> 1rem<span class="token punctuation">;</span>
  990. <span class="token punctuation">}</span></code></pre>
  991. <p><astro-island uid="Z15cCbU" prefix="r69" component-url="https://ishadeed.com/_astro/TwitterSpaces.U4-9hhPt.js" component-export="default" renderer-url="https://ishadeed.com/_astro/client.2wOxL1Aq.js" props='{"helper":[0,"Much better."],"variation":[0,"3"]}' ssr="" client="visible" opts='{"name":"TwitterSpaces","value":true}' await-children=""></astro-island></p>
  992. <p>While looking at a font in Adobe Fonts, I noticed an issue in the target size of the tag component.</p>
  993. <p><picture> <source srcset="https://ishadeed.com/_astro/adobe-fonts-tag-1.z4SgZnZ5_1Y2tLM.png" type="image/png"><source srcset="https://ishadeed.com/_astro/adobe-fonts-tag-1.z4SgZnZ5_Q2Ur1.webp" type="image/webp"> <img src="https://ishadeed.com/_astro/adobe-fonts-tag-1.z4SgZnZ5_1Y2tLM.png" alt="" loading="lazy" decoding="async"> </source></source></picture></p>
  994. <p>The target size of the tag is on the text only. As a user, you will expect
  995. that all of the tags is clickable. This is not good. I’m surprised to see
  996. that by Adobe.</p>
  997. <p><picture> <source srcset="https://ishadeed.com/_astro/adobe-fonts-tag-2.jlkOF7Ey_Z10pLdC.png" type="image/png"><source srcset="https://ishadeed.com/_astro/adobe-fonts-tag-2.jlkOF7Ey_Z28pkyo.webp" type="image/webp"> <img src="https://ishadeed.com/_astro/adobe-fonts-tag-2.jlkOF7Ey_Z10pLdC.png" alt="" loading="lazy" decoding="async"> </source></source></picture></p>
  998. <p>Here is the CSS for the component:</p>
  999. <pre class="language-html"><code is:raw="" class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</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>spectrum-Tags-item<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>listitem<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
  1000. <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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">&gt;</span></span>English<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">&gt;</span></span>
  1001. <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span></code></pre>
  1002. <pre class="language-css"><code is:raw="" class="language-css"><span class="token selector">.spectrum-Tags-item</span> <span class="token punctuation">{</span>
  1003. <span class="token property">display</span><span class="token punctuation">:</span> inline-flex<span class="token punctuation">;</span>
  1004. <span class="token property">align-items</span><span class="token punctuation">:</span> center<span class="token punctuation">;</span>
  1005. <span class="token property">padding</span><span class="token punctuation">:</span> 0 9px<span class="token punctuation">;</span>
  1006. <span class="token property">height</span><span class="token punctuation">:</span> 24px<span class="token punctuation">;</span>
  1007. <span class="token property">border</span><span class="token punctuation">:</span> 1px solid<span class="token punctuation">;</span>
  1008. <span class="token property">border-radius</span><span class="token punctuation">:</span> 4px<span class="token punctuation">;</span>
  1009. <span class="token punctuation">}</span></code></pre>
  1010. <p>To fix that, we need to:</p>
  1011. <ul>
  1012. <li>The padding should be added to the <code>&lt;a&gt;</code> instead.</li>
  1013. <li>Remove the fixed height Using a fixed height isn’t recommended. See <a href="https://defensivecss.dev/tip/fixed-sizes/">Fixed sizes on Defensive CSS</a>.</li>
  1014. </ul>
  1015. <pre class="language-css"><code is:raw="" class="language-css"><span class="token selector">.spectrum-Tags-item a</span> <span class="token punctuation">{</span>
  1016. <span class="token property">display</span><span class="token punctuation">:</span> inline-flex<span class="token punctuation">;</span>
  1017. <span class="token property">align-items</span><span class="token punctuation">:</span> center<span class="token punctuation">;</span>
  1018. <span class="token property">padding</span><span class="token punctuation">:</span> 4px 12px<span class="token punctuation">;</span>
  1019. <span class="token property">border</span><span class="token punctuation">:</span> 1px solid<span class="token punctuation">;</span>
  1020. <span class="token property">border-radius</span><span class="token punctuation">:</span> 4px<span class="token punctuation">;</span>
  1021. <span class="token punctuation">}</span></code></pre>
  1022. <p>Here is a video that compares the before and after change:</p>
  1023. <video poster="https://ishadeed.com/assets/target-size/videos/adobe-fonts-poster.png" src="https://ishadeed.com/assets/target-size/videos/adobe-fonts-tag.mp4" controls controlslist="nodownload"></video>
  1024. <h3 id="backblaze-navigation">Backblaze navigation</h3>
  1025. <p>I use Backblaze to back up my data and noticed something on the login page. The target size for the menu items is only on the text.</p>
  1026. <p><picture> <source srcset="https://ishadeed.com/_astro/backblaze-nav-1._NyIwUt3_1CKatB.png" type="image/png"><source srcset="https://ishadeed.com/_astro/backblaze-nav-1._NyIwUt3_Z2jgbA.webp" type="image/webp"> <img src="https://ishadeed.com/_astro/backblaze-nav-1._NyIwUt3_1CKatB.png" alt="" loading="lazy" decoding="async"> </source></source></picture></p>
  1027. <p>In the UI, it indicates that the whole item is clickable because there
  1028. are top and bottom borders.</p>
  1029. <p>Again, the fix is similar to the previous Adobe example. We need to add the padding to the <code>&lt;a&gt;</code> element.</p>
  1030. <p><picture> <source srcset="https://ishadeed.com/_astro/backblaze-nav-2.sVNwrzNN_YTu6d.png" type="image/png"><source srcset="https://ishadeed.com/_astro/backblaze-nav-2.sVNwrzNN_ZF9VyY.webp" type="image/webp"> <img src="https://ishadeed.com/_astro/backblaze-nav-2.sVNwrzNN_YTu6d.png" alt="" loading="lazy" decoding="async"> </source></source></picture></p>
  1031. <h3 id="dribbble-navigation">Dribbble navigation</h3>
  1032. <p>Yes, Dribbble! I found lots of interesting target size issues, so why not fix them? Even though Dribbble is full of beautiful but useless UIs with lots of shadows and trendy gradients.</p>
  1033. <p><picture> <source srcset="https://ishadeed.com/_astro/real-life-examples-1.l5TbdF-8_Z2iSl9l.png" type="image/png"><source srcset="https://ishadeed.com/_astro/real-life-examples-1.l5TbdF-8_Z25gxWy.webp" type="image/webp"> <img src="https://ishadeed.com/_astro/real-life-examples-1.l5TbdF-8_Z2iSl9l.png" alt="" loading="lazy" decoding="async"> </source></source></picture>I added a
  1034. CSS outline and noticed the following issues:</p>
  1035. <ul>
  1036. <li>Navigation item target size is too small</li>
  1037. <li>There is zero spacing between the navigation items. This isn’t good.</li>
  1038. <li>The search button is very small &amp; inconsistent with the messages and profile size.</li>
  1039. </ul>
  1040. <p>On mobile, it’s even worse. The width of each navigation item is dependent on the content length.</p>
  1041. <p><picture> <source srcset="https://ishadeed.com/_astro/real-life-examples-2.jMb3iBtB_Z1ft6yV.png" type="image/png"><source srcset="https://ishadeed.com/_astro/real-life-examples-2.jMb3iBtB_Z11Qjn9.webp" type="image/webp"> <img src="https://ishadeed.com/_astro/real-life-examples-2.jMb3iBtB_Z1ft6yV.png" alt="" loading="lazy" decoding="async"> </source></source></picture>I took it
  1042. further and mocked up how this UI can be viewed compared to human
  1043. fingers.</p>
  1044. <p><picture> <source srcset="https://ishadeed.com/_astro/real-life-examples-3.FyLgrdba_c8rEM.png" type="image/png"><source srcset="https://ishadeed.com/_astro/real-life-examples-3.FyLgrdba_pKeQz.webp" type="image/webp"> <img src="https://ishadeed.com/_astro/real-life-examples-3.FyLgrdba_c8rEM.png" alt="" loading="lazy" decoding="async"> </source></source></picture></p>
  1045. <p>Since the CSS is written via Flexbox, I was able to increase the CSS <code>gap</code>
  1046. and force the flex items to align normally.</p>
  1047. <p>The web version:</p>
  1048. <p><picture> <source srcset="https://ishadeed.com/_astro/real-life-examples-4.fg_d5aRG_mNNVS.png" type="image/png"><source srcset="https://ishadeed.com/_astro/real-life-examples-4.fg_d5aRG_AqB8F.webp" type="image/webp"> <img src="https://ishadeed.com/_astro/real-life-examples-4.fg_d5aRG_mNNVS.png" alt="" loading="lazy" decoding="async"> </source></source></picture></p>
  1049. <p>And the mobile version:</p>
  1050. <p><picture> <source srcset="https://ishadeed.com/_astro/real-life-examples-5.m05M50JQ_Z1cU5R6.png" type="image/png"><source srcset="https://ishadeed.com/_astro/real-life-examples-5.m05M50JQ_C5fim.webp" type="image/webp"> <img src="https://ishadeed.com/_astro/real-life-examples-5.m05M50JQ_Z1cU5R6.png" alt="" loading="lazy" decoding="async"> </source></source></picture></p>
  1051. <p>But wait… why the “Go Pro” item doesn’t have a padding from the bottom?
  1052. It’s because of this CSS:</p>
  1053. <pre class="language-css"><code is:raw="" class="language-css"><span class="token atrule"><span class="token rule">@media</span> <span class="token punctuation">(</span><span class="token property">max-width</span><span class="token punctuation">:</span> 949px<span class="token punctuation">)</span></span> <span class="token punctuation">{</span>
  1054. <span class="token selector">.nav-v2-main__item:last-child a</span> <span class="token punctuation">{</span>
  1055. <span class="token property">padding-bottom</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span>
  1056. <span class="token punctuation">}</span>
  1057. <span class="token punctuation">}</span></code></pre>
  1058. <p>This is not good. It’s a bad practice. Just keep the padding, please.</p>
  1059. <h3 id="dribbble-checkbox-component">Dribbble checkbox component</h3>
  1060. <p>Another example of Dribbble is the checkbox component. The target size is limited to the checkbox height only, while its container is large and is indicated with a border.</p>
  1061. <p>You might argue that this is just a container. For me, as a user, I assumed that the whole area was clickable.</p>
  1062. <p><picture> <source srcset="https://ishadeed.com/_astro/real-life-examples-6.Z8j5Mmi__2tNkAn.png" type="image/png"><source srcset="https://ishadeed.com/_astro/real-life-examples-6.Z8j5Mmi__Z2mL11L.webp" type="image/webp"> <img src="https://ishadeed.com/_astro/real-life-examples-6.Z8j5Mmi__2tNkAn.png" alt="" loading="lazy" decoding="async"> </source></source></picture></p>
  1063. <p>What I did is that I removed the padding from the outer container and added
  1064. it instead to the checkbox itself.</p>
  1065. <pre class="language-css"><code is:raw="" class="language-css"><span class="token selector">.checkbox-toggle-content</span> <span class="token punctuation">{</span>
  1066. <span class="token punctuation">}</span>
  1067. <span class="token selector">​ .checkbox-radio-label</span> <span class="token punctuation">{</span>
  1068. <span class="token property">padding</span><span class="token punctuation">:</span> 24px<span class="token punctuation">;</span>
  1069. <span class="token punctuation">}</span></code></pre>
  1070. <p><picture> <source srcset="https://ishadeed.com/_astro/real-life-examples-7.SIWrOZU2_ZdG2qR.png" type="image/png"><source srcset="https://ishadeed.com/_astro/real-life-examples-7.SIWrOZU2_Z4ff5.webp" type="image/webp"> <img src="https://ishadeed.com/_astro/real-life-examples-7.SIWrOZU2_ZdG2qR.png" alt="" loading="lazy" decoding="async"> </source></source></picture></p>
  1071. <p>Much better.</p>
  1072. <h2 id="target-size-cheatsheet">Target size cheatsheet</h2>
  1073. <p>I worked on a simple A4 cheatsheet that summarize the most important points in the article. It’s only for 7.00 USD and you can purchase it as a support for this guide.</p>
  1074. <p><picture> <source srcset="https://ishadeed.com/_astro/poster.XewPyGAC_ZKt6B2.jpg" type="image/jpg"><source srcset="https://ishadeed.com/_astro/poster.XewPyGAC_ZORkpD.webp" type="image/webp"> <img src="https://ishadeed.com/_astro/poster.XewPyGAC_ZKt6B2.jpg" alt="" loading="lazy" decoding="async"> </source></source></picture>
  1075. <astro-island uid="ZrxhmJ" prefix="r70" component-url="https://ishadeed.com/_astro/CheatSheetButton.Zrqypjtr.js" component-export="default" renderer-url="https://ishadeed.com/_astro/client.2wOxL1Aq.js" props="{}" ssr="" client="visible" opts='{"name":"CheatSheetButton","value":true}' await-children=""></astro-island></p>
  1076. <h2 id="outro">Outro</h2>
  1077. <p>When I started working on this article, I hadn’t expected it to grow that large. I initially wanted to make an interactive guide about the clickable area, but after a lot of research, I decided to call it “Target size”.</p>
  1078. <p>I already published about <a href="https://ishadeed.com/article/clickable-area/">this topic</a> five years ago, but felt the need to do it again, and I’m glad for that.</p>
  1079. <h3 id="thank-you">Thank you</h3>
  1080. <p>Thanks to my wife and partner, Kholoud, for her great support throughout the creation of this guide. I asked her about it every day for the past 6 weeks, and she didn’t kick me out of our home.</p>
  1081. <h3 id="resources">Resources</h3>
  1082. <ul>
  1083. <li><a href="https://craftcms.com/blog/accessible-target-sizes">Accessible Target Sizes</a></li>
  1084. <li><a href="https://adrianroselli.com/2019/06/target-size-and-2-5-5.html">Target Size and 2.5.5</a></li>
  1085. <li><a href="https://tetralogical.com/blog/2022/12/20/foundations-target-size">Foundations: target sizes</a></li>
  1086. <li><a href="https://www.nngroup.com/articles/touch-target-size/">Touch Targets on Touchscreens</a></li>
  1087. <li><a href="https://www.smashingmagazine.com/2012/02/finger-friendly-design-ideal-mobile-touchscreen-target-sizes/">Finger-Friendly Design: Ideal Mobile Touchscreen Target Sizes</a></li>
  1088. </ul>
  1089. <h3 id="thanks-to">Thanks to</h3>
  1090. <ul>
  1091. <li><a href="https://twitter.com/fvsch">Florens Verschelde</a> for shedding the light on the initial and fast movement of Fitt’ law.</li>
  1092. <li>The <a href="https://www.joshwcomeau.com/snippets/react-hooks/use-mouse-position/">useMousePosition</a> hook by Joshua Comeau</li>
  1093. <li><a href="https://kld.dev/building-table-of-contents/">Astro table of contents</a> by Kevin Drum</li>
  1094. <li>This <a href="https://www.smashingmagazine.com/2023/08/better-context-menus-safe-triangles/">article</a> by Costa Alexoglou on Smashing Magazine inspired me to the solution of safe triangles, but I used a clip-path instead.</li>
  1095. </ul>
  1096. </article>
  1097. <hr>
  1098. <footer>
  1099. <p>
  1100. <a href="/david/" title="Aller à l’accueil"><svg class="icon icon-home">
  1101. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-home"></use>
  1102. </svg> Accueil</a> •
  1103. <a href="/david/log/" title="Accès au flux RSS"><svg class="icon icon-rss2">
  1104. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-rss2"></use>
  1105. </svg> Suivre</a> •
  1106. <a href="http://larlet.com" title="Go to my English profile" data-instant><svg class="icon icon-user-tie">
  1107. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-user-tie"></use>
  1108. </svg> Pro</a> •
  1109. <a href="mailto:david%40larlet.fr" title="Envoyer un courriel"><svg class="icon icon-mail">
  1110. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-mail"></use>
  1111. </svg> Email</a> •
  1112. <abbr class="nowrap" title="Hébergeur : Alwaysdata, 62 rue Tiquetonne 75002 Paris, +33184162340"><svg class="icon icon-hammer2">
  1113. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-hammer2"></use>
  1114. </svg> Légal</abbr>
  1115. </p>
  1116. <template id="theme-selector">
  1117. <form>
  1118. <fieldset>
  1119. <legend><svg class="icon icon-brightness-contrast">
  1120. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-brightness-contrast"></use>
  1121. </svg> Thème</legend>
  1122. <label>
  1123. <input type="radio" value="auto" name="chosen-color-scheme" checked> Auto
  1124. </label>
  1125. <label>
  1126. <input type="radio" value="dark" name="chosen-color-scheme"> Foncé
  1127. </label>
  1128. <label>
  1129. <input type="radio" value="light" name="chosen-color-scheme"> Clair
  1130. </label>
  1131. </fieldset>
  1132. </form>
  1133. </template>
  1134. </footer>
  1135. <script src="/static/david/js/instantpage-5.1.0.min.js" type="module"></script>
  1136. <script>
  1137. function loadThemeForm(templateName) {
  1138. const themeSelectorTemplate = document.querySelector(templateName)
  1139. const form = themeSelectorTemplate.content.firstElementChild
  1140. themeSelectorTemplate.replaceWith(form)
  1141. form.addEventListener('change', (e) => {
  1142. const chosenColorScheme = e.target.value
  1143. localStorage.setItem('theme', chosenColorScheme)
  1144. toggleTheme(chosenColorScheme)
  1145. })
  1146. const selectedTheme = localStorage.getItem('theme')
  1147. if (selectedTheme && selectedTheme !== 'undefined') {
  1148. form.querySelector(`[value="${selectedTheme}"]`).checked = true
  1149. }
  1150. }
  1151. const prefersColorSchemeDark = '(prefers-color-scheme: dark)'
  1152. window.addEventListener('load', () => {
  1153. let hasDarkRules = false
  1154. for (const styleSheet of Array.from(document.styleSheets)) {
  1155. let mediaRules = []
  1156. for (const cssRule of styleSheet.cssRules) {
  1157. if (cssRule.type !== CSSRule.MEDIA_RULE) {
  1158. continue
  1159. }
  1160. // WARNING: Safari does not have/supports `conditionText`.
  1161. if (cssRule.conditionText) {
  1162. if (cssRule.conditionText !== prefersColorSchemeDark) {
  1163. continue
  1164. }
  1165. } else {
  1166. if (cssRule.cssText.startsWith(prefersColorSchemeDark)) {
  1167. continue
  1168. }
  1169. }
  1170. mediaRules = mediaRules.concat(Array.from(cssRule.cssRules))
  1171. }
  1172. // WARNING: do not try to insert a Rule to a styleSheet you are
  1173. // currently iterating on, otherwise the browser will be stuck
  1174. // in a infinite loop…
  1175. for (const mediaRule of mediaRules) {
  1176. styleSheet.insertRule(mediaRule.cssText)
  1177. hasDarkRules = true
  1178. }
  1179. }
  1180. if (hasDarkRules) {
  1181. loadThemeForm('#theme-selector')
  1182. }
  1183. })
  1184. </script>
  1185. </body>
  1186. </html>