Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

JS Fest 2019. Mauricio Palma. You can’t read this sentence - A11y automation

62 views

Published on

Exemplum illud exclusive…
How does it feel not being able to read the sentence above? This is what we are doing every day to about 360 million people with visual impairments.
As developers we don’t think very much about this numbers, and yet it’s still our responsibility to include as many people as possible in our applications. In this talk we will learn some important Javascript automation tasks e.g. enhanced color contrast and voice interfaces. Let us dive into some best practices and real-life examples to avoid making the same mistakes all over again in the future.

Published in: Education
  • Be the first to comment

  • Be the first to like this

JS Fest 2019. Mauricio Palma. You can’t read this sentence - A11y automation

  1. 1. 15 years ago…
  2. 2. 7 years ago…
  3. 3. 2012
  4. 4. She had a long history…
  5. 5. The grandma
  6. 6. 2012 From
  7. 7. To
  8. 8. They founded their label and…
  9. 9. The Man
  10. 10. Today
  11. 11. Mauricio Palma Co-founder and all kind of things @WDLK Product Engineer @sinnerschrader everywhere else @palmaswell
  12. 12. But
  13. 13. Why?
  14. 14. Back in The Day
  15. 15. Way harder!
  16. 16. opportunity
  17. 17. Nature of the web
  18. 18. We use our our abilities
  19. 19. You can’t read this sentence A11y automation April, 2019
  20. 20. That was awkward! Right?
  21. 21. Literally hurts!
  22. 22. 217 million
  23. 23. How to fix the numbers?
  24. 24. Blurry Vision
  25. 25. Key Takeaways Publish all information and content from your site in your HTML Use semantic HTML and the appropriate ARIA landmarks Write screen reader friendly markup Provide keyboard navigation
  26. 26. Partial vision loss
  27. 27. Key Takeaways Besides the mentioned semantic HTML and screen reader friendly markup You should follow a linear logical layout, that supports at least 200% magnification
  28. 28. Central vision loss
  29. 29. Key Takeaways Always put form elements in context. Meaning putting buttons, inputs, and notifications together in a logical manner For action elements in your interface use a good combination of colors, shapes, and text Do not rely on colors to convey meaning
  30. 30. Dyslexia
  31. 31. Key Takeaways Align your text to the left and keep a consistent layout Keep content short, clear and simple Consider producing materials in other formats (for example audio or video) Let the users change the contrast between background and text
  32. 32. Colorblindness
  33. 33. Key Takeaways Make sure that colors are not your only method of conveying important information.
  34. 34. Sunlight Bad lightning Temporary disabilities
  35. 35. Persona spectrum Permanent Temporary Situational
  36. 36. We usetechnologyto includemore people
  37. 37. A11y everywhere
  38. 38. You Your codeUnit testsShipCode Test
  39. 39. Friday night deployment?
  40. 40. Color Contrast Automation
  41. 41. AaAaAaAaAa AA || AAA
  42. 42. O(n^2)
  43. 43. 2500
  44. 44. Forward A11y color contrast automation github.com/palmaswell/forward
  45. 45. github.com/palmaswell/forward
  46. 46. Make it easier for users to see and hear content including separating foreground from background. Guideline 1.4 Distinguishable:
  47. 47. AA >= 4.5:1
  48. 48. AAA >= 7:1
  49. 49. AA >= 3:1 Large-scale text
  50. 50. AAA >= 4.5:1 Large-scale text
  51. 51. 24px || 19px bold Large-scale text
  52. 52. Logos and brand Other exceptions
  53. 53. Inactive UI elements Other exceptions
  54. 54. Can your read this? Color contrast 1.6 ratio
  55. 55. Can your read this? Color contrast 8.05 ratio AAA
  56. 56. Note 1: For the sRGB colorspace, the relative luminance of a color is defined as L = 0.2126 * R + 0.7152 * G + 0.0722 * B where R, G and B are defined as: if RsRGB <= 0.03928 then R = RsRGB/12.92 else R = ((RsRGB+0.055)/1.055) ^ 2.4 if GsRGB <= 0.03928 then G = GsRGB/12.92 else G = ((GsRGB+0.055)/1.055) ^ 2.4 if BsRGB <= 0.03928 then B = BsRGB/12.92 else B = ((BsRGB+0.055)/1.055) ^ 2.4 WCAG definition of relative luminance
  57. 57. Excuse me?
  58. 58. Luminance
  59. 59. Relative Luminance
  60. 60. 0
  61. 61. 1
  62. 62. Note 1: For the sRGB color space, the relative luminance of a color is defined as L = 0.2126 * R + 0.7152 * G + 0.0722 * B where R, G and B are defined as: WCAG definition of relative luminance
  63. 63. standardized by the International Electrotechnical Commission sRGB
  64. 64. sRGB
  65. 65. Our visual system
  66. 66. Works in a similar way Default color space Visual SystemWeb Browsers
  67. 67. Chromaticity | Chromaticity | Red | Green | Blue | White point | |--------------|---------|-----------|----------|--------------| | x | 0.6400 | 0.3000 | 0.1500 | 0.3127 | | y | 0.3300 | 0.6000 | 0.0600 | 0.3290 | | Y | 0.2126 | 0.7152 | 0.0722 | 1.0000 |
  68. 68. export enum YValues { r = 0.2126, g = 0.7152, b = 0.0722, } xyY color space Step 1 Y values represent the luminance of a color entry
  69. 69. export function calculateRGBEntry( entry: number, y: Type.YValues): number { const average = entry / 255; return average <= 0.03928 ? average / 12.92 * y : ((average + 0.055) / 1.055 ) ** 2.4 * y; }; Color contrast Step 2
  70. 70. Color contrast Step 2 export function luminance( sRGB: Type.RGB ): number { return calculateRGBEntry(sRGB[0], Type.YValues.r) + calculateRGBEntry(sRGB[1], Type.YValues.g) + calculateRGBEntry(sRGB[2], Type.YValues.b); }
  71. 71. Color contrast Step 2 export function contrastRatio( sRGB: Type.RGB, sRGB2: Type.RGB): number { const light = Math.max(luminance(sRGB), luminance(sRGB2)); const dark = Math.min(luminance(sRGB), luminance(sRGB2)); return +((light + 0.05) / (dark + 0.05)).toFixed(2); }
  72. 72. N colors Aa Aa Aa Aa [ ]
  73. 73. A11y everywhere
  74. 74. Quicksort fast sorting algorithm
  75. 75. SortStep 3 export function quickSort( arr: Type.Color[], cb: (sRGB: Type.RGB) => number, lo: number, hi: number ): void { if (lo < hi) { const pivot = cb(arr[Math.floor((lo + hi) / 2)].rgb); const p = partition(arr, lo, hi, pivot, cb); quickSort(arr, cb, lo, p.hi); quickSort(arr, cb, p.lo, hi); } } export function sort( arr: Type.Color[], cb: (sRGB: Type.RGB) => number) { return quickSort(arr, cb, 0, arr.length - 1); }
  76. 76. SortStep 3 export function quickSort( arr: Type.Color[], cb: (sRGB: Type.RGB) => number, lo: number, hi: number ): void { if (lo < hi) { const pivot = cb(arr[Math.floor((lo + hi) / 2)].rgb); const p = partition(arr, lo, hi, pivot, cb); quickSort(arr, cb, lo, p.hi); quickSort(arr, cb, p.lo, hi); } } export function sort( arr: Type.Color[], cb: (sRGB: Type.RGB) => number) { return quickSort(arr, cb, 0, arr.length - 1); }
  77. 77. SortStep 3 export function quickSort( arr: Type.Color[], cb: (sRGB: Type.RGB) => number, lo: number, hi: number ): void { if (lo < hi) { const pivot = cb(arr[Math.floor((lo + hi) / 2)].rgb); const p = partition(arr, lo, hi, pivot, cb); quickSort(arr, cb, lo, p.hi); quickSort(arr, cb, p.lo, hi); } } export function sort( arr: Type.Color[], cb: (sRGB: Type.RGB) => number) { return quickSort(arr, cb, 0, arr.length - 1); }
  78. 78. SortStep 3 export function partition( arr: Type.Color[], lo: number, hi: number, pivot: number, cb: (sRGB: Type.RGB) => number): { lo: number, hi: number} { if (lo <= hi) { if (cb(arr[lo].rgb) < pivot) { return partition(arr, lo + 1, hi, pivot, cb); } if (cb(arr[hi].rgb) > pivot) { return partition(arr, lo, hi - 1, pivot, cb); } if (lo <= hi) { swap(arr, lo, hi); return partition(arr, lo + 1, hi - 1, pivot, cb); } } return { lo, hi }; }
  79. 79. SortStep 3 export function quickSort( arr: Type.Color[], cb: (sRGB: Type.RGB) => number, lo: number, hi: number ): void { if (lo < hi) { const pivot = cb(arr[Math.floor((lo + hi) / 2)].rgb); const p = partition(arr, lo, hi, pivot, cb); quickSort(arr, cb, lo, p.hi); quickSort(arr, cb, p.lo, hi); } } export function sort( arr: Type.Color[], cb: (sRGB: Type.RGB) => number) { return quickSort(arr, cb, 0, arr.length - 1); }
  80. 80. AaAaAaAaAa QuicksortStep 3
  81. 81. Aa Aa AaAaAa Sorted by luminance Step 3
  82. 82. Binary Search efficient algorithm that searches a sorted list
  83. 83. Aa Aa AaAaAa Binary Search Step 4 AAA color contrast check Match!
  84. 84. SearchStep 4 function lowerSearch<T extends QuickSearch>( arr: Array<T>, el: T, val: number, lo: number, hi: number): number | [] { const mid = Math.floor((lo + hi) / 2); const prev = mid - 1; const next = mid + 1; const midRatio = contrastRatio(arr[mid].rgb, el.rgb); if (lo < hi) { if (midRatio > val && prev >= 0) { if (contrastRatio(arr[prev].rgb, el.rgb) < val) { return mid; } return lowerSearch(arr, el, val, lo, mid); } if (midRatio < val && next < arr.length) { if (contrastRatio(arr[next].rgb, el.rgb) > val) { return next; } return lowerSearch(arr, el, val, mid, hi) } if (mid === val) { return mid; } } return []; }; export function search( arr: Type.Color[], el: Type.Color, val: Type.A11yRatio, type?: Type.Search): number | [] { if (type === Type.Search.upper) { return upperSearch(arr, el, val, 0, arr.length); } return lowerSearch(arr, el, val, 0, arr.length); };
  85. 85. SearchStep 4 function lowerSearch<T extends QuickSearch>( arr: Array<T>, el: T, val: number, lo: number, hi: number): number | [] { const mid = Math.floor((lo + hi) / 2); const prev = mid - 1; const next = mid + 1; const midRatio = contrastRatio(arr[mid].rgb, el.rgb); if (lo < hi) { if (midRatio > val && prev >= 0) { if (contrastRatio(arr[prev].rgb, el.rgb) < val) { return mid; } return lowerSearch(arr, el, val, lo, mid); } if (midRatio < val && next < arr.length) { if (contrastRatio(arr[next].rgb, el.rgb) > val) { return next; } return lowerSearch(arr, el, val, mid, hi) } if (mid === val) { return mid; } } return []; }; export function search( arr: Type.Color[], el: Type.Color, val: Type.A11yRatio, type?: Type.Search): number | [] { if (type === Type.Search.upper) { return upperSearch(arr, el, val, 0, arr.length); } return lowerSearch(arr, el, val, 0, arr.length); };
  86. 86. SearchStep 4 function lowerSearch<T extends QuickSearch>( arr: Array<T>, el: T, val: number, lo: number, hi: number): number | [] { const mid = Math.floor((lo + hi) / 2); const prev = mid - 1; const next = mid + 1; const midRatio = contrastRatio(arr[mid].rgb, el.rgb); if (lo < hi) { if (midRatio > val && prev >= 0) { if (contrastRatio(arr[prev].rgb, el.rgb) < val) { return mid; } return lowerSearch(arr, el, val, lo, mid); } if (midRatio < val && next < arr.length) { if (contrastRatio(arr[next].rgb, el.rgb) > val) { return next; } return lowerSearch(arr, el, val, mid, hi) } if (mid === val) { return mid; } } return []; }; export function search( arr: Type.Color[], el: Type.Color, val: Type.A11yRatio, type?: Type.Search): number | [] { if (type === Type.Search.upper) { return upperSearch(arr, el, val, 0, arr.length); } return lowerSearch(arr, el, val, 0, arr.length); };
  87. 87. Enhanced Color
  88. 88. Enhanced colors Step 5 export function createEnhanced( rawColor: Type.Color, aaa: Type.Color[] | [], aa: Type.Color[] | [] ): Type.colorEnhanced { const rgb = rawColor.rgb; return { name: rawColor.name, rgb, aaa, aa, toRGB(): string { return toRGBString(rgb); }, toHEX(): string { return tinyColor(toRGBString(rgb)).toHexString(); }, toHSL(): string { return tinyColor(toRGBString(rgb)).toHslString(); }, toRGBA(alpha: number): string { const color = tinyColor(toRGBString(rgb)); color.setAlpha(alpha); return color.toRgbString() } } }
  89. 89. Enhanced colors Step 5 export function createEnhanced( rawColor: Type.Color, aaa: Type.Color[] | [], aa: Type.Color[] | [] ): Type.colorEnhanced { const rgb = rawColor.rgb; return { name: rawColor.name, rgb, aaa, aa, toRGB(): string { return toRGBString(rgb); }, toHEX(): string { return tinyColor(toRGBString(rgb)).toHexString(); }, toHSL(): string { return tinyColor(toRGBString(rgb)).toHslString(); }, toRGBA(alpha: number): string { const color = tinyColor(toRGBString(rgb)); color.setAlpha(alpha); return color.toRgbString() } } }
  90. 90. Colors Under Control
  91. 91. Demo
  92. 92. It’s open github.com/palmaswell/forward Use it, play with it, make it better!
  93. 93. Benefits New opportunities Offer utility and elegance Profitable Legal compliance
  94. 94. Before: 10% (imaginary number) Middle: x100% After: x1000% A11y costs factors
  95. 95. Questions?
  96. 96. Thanks!Github: @palmaswell Twitter: @palmaswell mauricio.palma@sinnerschrader.com

×