diff --git a/content/2017-01-18-keeping-lyft-app-accessible.md b/content/2017-01-18-keeping-lyft-app-accessible.md new file mode 100644 index 0000000..f2e310e --- /dev/null +++ b/content/2017-01-18-keeping-lyft-app-accessible.md @@ -0,0 +1,82 @@ +Title: Keeping the Lyft iOS App Accessible +Date: 2017-01-18 +Summary: Explanation on some strategies in how Lyft keeps its iOS app accessible + +!!! note + This article was written by me but originally published on the [Lyft + Engineering Blog](https://eng.lyft.com/keeping-lyft-accessible-53155f0098b9). + +At Lyft we’re in a unique position: every day we work on an app that affects +people in the real, physical world — not just the digital one. This has an +especially large impact on visually impaired users, because our app is +easier to use than the real world alternatives — navigating a train station +or queueing up at a taxi stand. Lyft aims to make a product that is +accessible and easy to use for all of our users. + +## Accessibility at Lyft + +If an app uses Apple’s standard controls and UI elements, it will likely already +work well with VoiceOver; however, Lyft’s UI deviates enough from standard +controls such that we don’t get that benefit out of the box. Another +complication is that many portions of the app are controlled by the server, +including a lot of the text for buttons and labels. Making these UI elements +compatible with VoiceOver often requires tweaking them manually, even if we use +native controls. + +Since we try to keep all of our UI-related code in Interface Builder, and +VoiceOver is just another form of UI, we have written a simple `UIView` +extension that allows us to enable accessibility on most of our UI directly from +Interface Builder. In this extension, we add 2 properties to `UIView`: + +* `accessibilitySources`: An `IBOutletCollection` of `UIView`s +* `accessibilityFormat`: a simple string that represents the format of the + accessibility label, which is subsequently passed to `String(format:)`. Every + occurrence of `%@` in this string will be replaced with the next element of + the `accessibilitySources`, and `[self]` will automatically be replaced with + the current view’s accessibilityLabel. + +We can ctrl + drag UI elements into the `accessibilitySources`, and by marking +`accessibilityFormat` with `@IBInspectable` we can specify the string and the +sources all from Interface Builder, keeping the code clean and to the point. + +For example, we have an accessibility format on the UILabel that displays the +selected ride mode: + +![Setting the accessibility format](/images/AccessibilityFormat.png) + +`[self]` is replaced by the accessibility label, which for `UILabel`s is the +text of the label itself. `%@` is replaced by an accessibility source, which we +can set up like this: + +![Setting the accessibility sources](/images/AccessibilitySource.png) + +By disabling the accessibility label for the subtitle, the top label’s +accessibility label will read “Selected ride mode: Line. Carpool, 2 people +maximum.” without touching any code at all. + +## Changing the process + +As we ramped up our VoiceOver efforts, we wanted to assess whether the changes +we made had a meaningful impact to end-users. As developers, we’re too familiar +with our own apps to make an honest call about their usability, and when it +comes to visual impairment, even a standard usability review could brush over +things that would be challenging to a blind or a visually impaired user. + +This is why we’re working with a dedicated accessibility expert, himself a +native VoiceOver user, to constantly validate our work. All the feedback we’ve +been getting from VoiceOver users have further motivated our investment in +accessibility. We have optimized VoiceOver in the main flows of our app, and we +run weekly regression tests to ensure consistency and stability. Our VoiceOver +user also works directly with QA engineers, to let them know what he is looking +for and what is missing. + +Over the last few months, we have made many improvements in various parts of our +app, but also in how seriously we take VoiceOver-related bugs: we block new +releases if VoiceOver users experience bugs when going through a ride. All new +features are expected to be optimized for VoiceOver from the start, and we are +working hard to optimize existing features as well. + +We think of accessibility as part of the user interface, just like labels, +buttons, and text fields. By having our developers implement support for +VoiceOver as part of the initial feature, our visually impaired users will be +able to use these features as easily as our regular users. diff --git a/content/images/AccessibilityFormat.png b/content/images/AccessibilityFormat.png new file mode 100644 index 0000000..fcd9ce0 Binary files /dev/null and b/content/images/AccessibilityFormat.png differ diff --git a/content/images/AccessibilitySource.png b/content/images/AccessibilitySource.png new file mode 100644 index 0000000..94f9866 Binary files /dev/null and b/content/images/AccessibilitySource.png differ diff --git a/pelicanconf.py b/pelicanconf.py index 1bb73ce..db49813 100644 --- a/pelicanconf.py +++ b/pelicanconf.py @@ -14,6 +14,7 @@ 'markdown.extensions.extra': {}, 'markdown.extensions.meta': {}, 'markdown.extensions.toc': {}, + 'markdown.extensions.admonition': {}, }, 'output_format': 'html5', } diff --git a/theme/static/css/style.css b/theme/static/css/style.css index aaf6501..539d7ed 100644 --- a/theme/static/css/style.css +++ b/theme/static/css/style.css @@ -171,7 +171,6 @@ dl#archive dd { margin: 20px 0 20px 40px; } - @media only screen and (max-width: 320px) { dl#archive dd { margin-left: 0; @@ -215,6 +214,32 @@ blockquote { color: #777; } +.admonition { + border-left: 5px solid #4CAF50; /* Accent color for the border */ + background-color: #f9f9f9; /* Light background */ + padding: 15px; + margin: 20px 0; + margin-bottom: 50px; + border-radius: 8px; + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); +} + +.admonition-title { + font-weight: bold; + font-size: 1.1em; + margin-bottom: 10px; + color: #333; +} + +.admonition p { + margin: 0; +} + +img { + max-width: 100%; + height: auto; +} + div#disqus_thread { margin-top: 50px; padding: 10px;