Slide 1

Slide 1 text

by Feli Bernutz, Pragma Conference 2024 How to ensure accessibility in design system components Designing APIs

Slide 2

Slide 2 text

No content

Slide 3

Slide 3 text

Has anyone followed the Olympics in Paris this year? !

Slide 4

Slide 4 text

Has anyone followed the Paralympics? !

Slide 5

Slide 5 text

No content

Slide 6

Slide 6 text

Ever heard of the Vision Pad?

Slide 7

Slide 7 text

Source: Olympics – Accessibility Vision Pad

Slide 8

Slide 8 text

Accessibility is a human right.

Slide 9

Slide 9 text

My name is Feli Bernutz (she/her) • iOS engineer at @Spotify • Nuremberg, Germany " • Sketchnoting • Sports # $% • Pineapple on pizza & Heyy '

Slide 10

Slide 10 text

No content

Slide 11

Slide 11 text

() It’s the process of architecting and structuring software to meet specific functional and technical requirements, like • scalability • security • performance System Design

Slide 12

Slide 12 text

*+ It’s a collection of • reusable UI components • style guidelines • design principles to ensure consistency across a product’s user interface. Design System

Slide 13

Slide 13 text

Source: Apple Design Website Apple’s design system

Slide 14

Slide 14 text

Source: Apple's Human Interface Guidelines

Slide 15

Slide 15 text

Apple’s system UI components are accessible by default.

Slide 16

Slide 16 text

So why not just use what Apple provides? ,

Slide 17

Slide 17 text

No content

Slide 18

Slide 18 text

No content

Slide 19

Slide 19 text

✅ Sufficient Color Contrast Encore Color Theme

Slide 20

Slide 20 text

✅ Don’t exclusively rely on color to convey meaning Multi modality

Slide 21

Slide 21 text

UI is just one option to let the user interact with your product.

Slide 22

Slide 22 text

User Information

Slide 23

Slide 23 text

Information User Shuffle mode enabled vs. Shuffle mode disabled

Slide 24

Slide 24 text

User Information Action

Slide 25

Slide 25 text

. Hearing / Taste 0 Smell 1 Vision ✋ Touch

Slide 26

Slide 26 text

. Hearing / Taste 0 Smell 1 Vision ✋ Touch

Slide 27

Slide 27 text

No content

Slide 28

Slide 28 text

1. 114: “Accessibility on Apple’s platforms”, with … 2. Swift by Sundell 3. Sommer Panage returns to the show to … 4. Mar 21, 2022, 1 hr, 12 min 5. Remove from Your Library 6. Download 7. Share 8. More options 9. Play 10. Paul Van Workum - App Accessibility Expert 11. The Digital Accessibility Podcast 12. In this episode, Joe James is joined by … 13. Apr 25, 2023, 34 min 14. Remove from Episodes 15. Download 16. Share 17. More options 18. Play

Slide 29

Slide 29 text

1. 114: “Accessibility on Apple’s platforms”, with special guest Sommer Panage. Swift by Sundell, Published on 21. March 2022, Episode length is 1 hr, 12 min, Added to Your Library, Button, View episode details 2. Paul Van Workum - App Accessibility Expert. The Digital Accessibility Podcast, Published on 25. April 2023, Episode length is 34 min, Added to Your Library, Button, View episode details 3. Special: Daniel Devesa Derksen-Staats. AppForce1: news and info for iOS app developers, Published on 14. January 2021, Episode length is 41 min, Added to Your Library, Button, View episode details

Slide 30

Slide 30 text

Source: Laws of UX

Slide 31

Slide 31 text

Encore’s List Row component

Slide 32

Slide 32 text

How to design the API?

Slide 33

Slide 33 text

No content

Slide 34

Slide 34 text

No content

Slide 35

Slide 35 text

Progress over perfection. Guidelines to consider

Slide 36

Slide 36 text

There’s no such thing as perfect accessibility. Guidelines to consider

Slide 37

Slide 37 text

Provide different levels of abstraction. Guidelines to consider

Slide 38

Slide 38 text

Source: Jason Fried – The Obvious, the Easy, and the Possible (2011) & Jake Wharton – Slope-intercept library design (2022) Possible 4 % Easy 16 % Obvious 80 % Different levels of abstractions

Slide 39

Slide 39 text

✨ DONE ✨ 1. Add out-of-the-box solution is it good enough? yes no The Game Plan

Slide 40

Slide 40 text

4 // Default accessibility props are set internally var listRow = Encore.ListRow(content: .init( )) Out of the box solution The Game Plan – Step 1

Slide 41

Slide 41 text

4 // Default accessibility props are set internally var listRow = Encore.ListRow(content: .init( media: .image(UIImage), )) Out of the box solution The Game Plan – Step 1

Slide 42

Slide 42 text

4 Most Shared Podcast in Sweden // Default accessibility props are set internally var listRow = Encore.ListRow(content: .init( media: .image(UIImage), pretitle: .text("Most Shared Podcast in Sweden"), )) Out of the box solution The Game Plan – Step 1

Slide 43

Slide 43 text

4 Most Shared Podcast in Sweden, Damn Fine Coffee // Default accessibility props are set internally var listRow = Encore.ListRow(content: .init( media: .image(UIImage), pretitle: .text("Most Shared Podcast in Sweden"), title: .text("Damn Fine Coffee"), )) Out of the box solution The Game Plan – Step 1

Slide 44

Slide 44 text

4 Most Shared Podcast in Sweden, Damn Fine Coffee, Actions available // Default accessibility props are set internally var listRow = Encore.ListRow(content: .init( media: .image(UIImage), pretitle: .text("Most Shared Podcast in Sweden"), title: .text("Damn Fine Coffee"), trailing: .button(Encore.Button), )) Out of the box solution The Game Plan – Step 1

Slide 45

Slide 45 text

4 Most Shared Podcast in Sweden, Damn Fine Coffee, Actions available // Default accessibility props are set internally var listRow = Encore.ListRow(content: .init( media: .image(UIImage), pretitle: .text("Most Shared Podcast in Sweden"), title: .text("Damn Fine Coffee"), trailing: .button(Encore.Button), )) Out of the box solution The Game Plan – Step 1

Slide 46

Slide 46 text

// Simplified internal logic func setup(content: Encore.Content) { accessibilityIdentifier = "Encore.ListRow" } Internal implementation The Game Plan – Step 1

Slide 47

Slide 47 text

// Simplified internal logic func setup(content: Encore.Content) { accessibilityIdentifier = "Encore.ListRow" isAccessibilityElement = true } Internal implementation The Game Plan – Step 1

Slide 48

Slide 48 text

// Simplified internal logic func setup(content: Encore.Content) { accessibilityIdentifier = "Encore.ListRow" isAccessibilityElement = true // Compute accessibility information by using the available content accessibilityLabel = content.accessibilityLabel } Internal implementation The Game Plan – Step 1

Slide 49

Slide 49 text

// Simplified internal logic func setup(content: Encore.Content) { accessibilityIdentifier = "Encore.ListRow" isAccessibilityElement = true // Compute accessibility information by using the available content accessibilityLabel = content.accessibilityLabel } Internal implementation The Game Plan – Step 1 extension Encore.Content { var accessibilityLabel: String { [ pretitle?.accessibilityLabel, title.accessibilityLabel, subtitle?.accessibilityLabel, media?.accessibilityLabel, leading?.accessibilityLabel, trailing?.accessibilityLabel, footer?.accessibilityLabel, ] .compactMap { $0 } .filter { !$0.isEmpty } .joined(separator: ", ") .replacingOccurrences(of: " • ", with: ", ") } }

Slide 50

Slide 50 text

// Simplified internal logic func setup(content: Encore.Content) { accessibilityIdentifier = "Encore.ListRow" isAccessibilityElement = true // Compute accessibility information by using the available content accessibilityLabel = content.accessibilityLabel accessibilityCustomActions = content.buttons.compactMap { $0.accessibilityCustomAction } } Internal implementation The Game Plan – Step 1

Slide 51

Slide 51 text

// Simplified internal logic func setup(content: Encore.Content) { accessibilityIdentifier = "Encore.ListRow" isAccessibilityElement = true // Compute accessibility information by using the available content accessibilityLabel = content.accessibilityLabel accessibilityCustomActions = content.buttons.compactMap { $0.accessibilityCustomAction } accessibilityCustomContent = content.accessibilityCustomContent } Internal implementation The Game Plan – Step 1

Slide 52

Slide 52 text

// Simplified internal logic func setup(content: Encore.Content) { accessibilityIdentifier = "Encore.ListRow" isAccessibilityElement = true // Compute accessibility information by using the available content accessibilityLabel = content.accessibilityLabel accessibilityCustomActions = content.buttons.compactMap { $0.accessibilityCustomAction } accessibilityCustomContent = content.accessibilityCustomContent accessibilityUserInputLabels = content.accessibilityUserInputLabels } Internal implementation The Game Plan – Step 1

Slide 53

Slide 53 text

// Simplified internal logic public var body: some View { makeBody(with: content) .accessibilityIdentifier("Encore.ListRow") .accessibilityElement(children: .combine) } SwiftUI internal implementation The Game Plan – Step 1

Slide 54

Slide 54 text

✨ DONE ✨ 1. Add out-of-the-box solution is it good enough? yes no 2. Add explicit API for accessibility info is it good enough? no yes The Game Plan

Slide 55

Slide 55 text

Box.

Slide 56

Slide 56 text

Encore’s Box component The Game Plan – Step 2

Slide 57

Slide 57 text

var box = Encore.Box( ) Explicit API for accessibility info The Game Plan – Step 2

Slide 58

Slide 58 text

4 var box = Encore.Box( contentView: ContentView(), ) Explicit API for accessibility info The Game Plan – Step 2

Slide 59

Slide 59 text

4 Soft Pop Hits, Playlist, by Spotify var box = Encore.Box( contentView: ContentView(), accessibilityLabel: "Soft Pop Hits, Playlist, by Spotify", ) Explicit API for accessibility info The Game Plan – Step 2

Slide 60

Slide 60 text

4 Soft Pop Hits, Playlist, by Spotify, Currently playing var box = Encore.Box( contentView: ContentView(), accessibilityLabel: "Soft Pop Hits, Playlist, by Spotify", accessibilityValue: "Currently playing", ) Explicit API for accessibility info The Game Plan – Step 2

Slide 61

Slide 61 text

4 Soft Pop Hits, Playlist, by Spotify, Currently playing var box = Encore.Box( contentView: ContentView(), accessibilityLabel: "Soft Pop Hits, Playlist, by Spotify", accessibilityValue: "Currently playing", accessibilityHint: nil, ) Explicit API for accessibility info The Game Plan – Step 2

Slide 62

Slide 62 text

4 Soft Pop Hits, Playlist, by Spotify, Currently playing, Button var box = Encore.Box( contentView: ContentView(), accessibilityLabel: "Soft Pop Hits, Playlist, by Spotify", accessibilityValue: "Currently playing", accessibilityHint: nil, accessibilityTraits: .button, ) Explicit API for accessibility info The Game Plan – Step 2

Slide 63

Slide 63 text

4 Soft Pop Hits, Playlist, by Spotify, Currently playing, Button, Actions available var box = Encore.Box( contentView: ContentView(), accessibilityLabel: "Soft Pop Hits, Playlist, by Spotify", accessibilityValue: "Currently playing", accessibilityHint: nil, accessibilityTraits: .button, accessibilityCustomActions: [ playButton.accessibilityCustomAction, ] ) Explicit API for accessibility info The Game Plan – Step 2

Slide 64

Slide 64 text

4 Soft Pop Hits, Playlist, by Spotify, Currently playing, Button, Actions available var box = Encore.Box( contentView: ContentView(), accessibilityLabel: "Soft Pop Hits, Playlist, by Spotify", accessibilityValue: "Currently playing", accessibilityHint: nil, accessibilityTraits: .button, accessibilityCustomActions: [ playButton.accessibilityCustomAction, ] ) Explicit API for accessibility info The Game Plan – Step 2

Slide 65

Slide 65 text

✨ DONE ✨ 1. Add out-of-the-box solution is it good enough? yes no 2. Add explicit API for accessibility info is it good enough? no yes 3. Create Guidelines is it good enough? yes The Game Plan

Slide 66

Slide 66 text

Guidelines The Game Plan – Step 3

Slide 67

Slide 67 text

Live Demo with assistive technology

Slide 68

Slide 68 text

Summary

Slide 69

Slide 69 text

linktr.ee/felibe444