Ever needed to squint to see some text? How about feeling unsure where a button is because it’s indistinguishable from the background color? Accessible technology means that the product we provide works for everyone, regardless of what their abilities may be. We want Slack to be the place where work happens and this is one of the major considerations we think about in our development.

In order to create an accessible application, we must break down the status quo of what we deem standard for sizes, colorings, and more. Making a mobile app accessible means providing options that let a user decide what best suits their needs. For example, we could provide a large font for everyone, but that would frustrate users who may benefit from a smaller size.

We’ve done a lot of things to improve the usability of the iOS app, and we picked five to describe here:

  • Disabling animations in GIFs and emojis
  • Font scaling
  • VoiceOver
  • Contrast
  • Testing

Throughout this post we’ll share the tools, websites, and frameworks we use while developing new accessibility features and improving existing infrastructure along the way.

Disabling Animations

Opening Accessibility tab in the iOS app

Animated GIFs and emojis are extremely popular in Slack, and help to create rich communication with your teammates. However, for some users, such as those who experience vertigo or epilepsy, seeing these animations can be a jarring or uncomfortable experience, and we wanted to let them disable animations. Also, some users just don’t want to be distracted with animations in their message view. So, how do we tone down the :party_parrot: party?

When a preference is set to disable animations, we post an NSNotification that informs any observers, such as the main chat view, to clear out the current text so we can replace animated emojis and GIFs with the static representation of that emoji or GIF. To create this asset we reference the FLAnimatedImage framework, using the FLAnimatedImage.posterImage object to check if the preference to freeze animations is on and then passing the contents of the photo data to the poster image to create the static element. (The FLAnimatedImage is valuable for more than accessibility — it’s also what we use to make sure images are animated when they should be).

We want users to only have to enable this setting once to have a consistent experience across all their Slack apps. To sync between clients, we use our real-time messaging websocket connection to broadcast changes. Upon receiving the updates, we compare the key stored in NSUserDefaults to the updated preference, and then either update or maintain the state. This works in both directions for the clients, from device to desktop and vice versa.

Font Scaling

Squinting while looking at text is annoying — we want to help with that. Whether a user has near or farsighted vision, we’ve got them covered with our font adjustments.

To help with these and other visual impairment issues, users can adjust the scale within the chat view of the app to determine which size is comfortable while watching a sample message dynamically update with each tick.

Font Scaling in Slack

After a user chooses a setting, we need to update the default scale throughout the app. The flow goes like this:

  • We post a notification of the size of the scale chosen.
  • When a view observes the notification, we prepare for the new message content dimensions by clearing the cached size and animated emoji view bounds.
  • We update the appearance of text and links within the message, thread content, and emojis in our main messaging views.

We use our layout helper that monitors all of our screen scaling, font sizing, and phone-size-dependent boundaries to check if this value has been updated across the app and adjust it as necessary so that the font size is maintained no matter which workspace the user is on. Then if VoiceOver is running, we read the new size aloud (more detail on VoiceOver in the following section). Once the notification about the change has posted we update any areas that contain text, including the main chat view, files view, and text processing manager.

While we do our own scaling within the app, we also want to maintain any iOS settings that a user has customized. In the iOS settings there’s an option to enable Larger Text; if you enable this for your device, and the app is built to support Dynamic Type, the font adjusts in the message view. We’ve taken care to support Dynamic Type in all the areas we perform our own font manipulation, respecting font style by using Apple’s UIFontDescriptor and updating based on the UIContentSizeCategoryDidChangeNotification;The necessary changes are magically (well, programmatically) made to the UI, working with the font chosen first by the OS and second enhanced by our font sizing.

We’re still working to support font changes across the entire app. Currently, menus and sign in don’t support font changes. The sign in views are especially challenging, since we don’t know your Slack-specific settings before you log-in, and need to rely on device settings. Stay tuned for future updates!

VoiceOver

One of iOS’s built-in accessibility offerings is VoiceOver, a screenreader that can not only read text but also describe functionality aloud to a user. We use Apple’s Accessibility API to ensure users can navigate our app with VoiceOver. VoiceOver knows how to interact with iOS’s default elements and views, but for custom ones – of which Slack’s app has many – it needs help in the form of labels (for descriptions), traits (what type of element, such as a button, link, static text, or image), and values. For example, if there’s a slider on the screen, the label would be “Sound,” the trait would be UIAccessibilityTraitAdjustable, and the value could be “40%”. Traits are what VoiceOver uses to let the user know what element they’re currently focused on and what type of interaction is possible for that element; UIAccessibilityConstants provides a list of all the different traits that are possible to set for elements in your views.

On an element such as a button we can set both an accessibilityLabel, with an NSLocalizedString with the title of the button, and a UIAccessibilityTraits attribute, which in this case we would want to beUIAccessibilityTraitButton.

self.accessibilityTraits = UIAccessibilityTraitButton
self.accessibilityLabel = self.emoji.alias
self.accessibilityHint = NSLocalizedString(@"Tap to pick this emoji", nil)

Each emoji in the picker is a button that can be selected to react to a message. The label is the name of the emoji — for example, the following emoji’s alias is :smile: — and the hint allows the VoiceOver user to know what will happen when they select the emoji.

Color and Contrast

Channel header

Our designers here at Slack think deeply about color to make sure text on a background is legible, buttons are easily discovered, and objects can be differentiated. Since all of the interfaces need their own palettes, like our channel header shown above, they crafted a makeover that maintains a more uniform color set and differentiation between items. The designers audited all the colors used in the apps and reduced them to a more manageable number so that they were more consistent and to ensure proper contrast was always possible, while still keeping enough colors for each feature’s design vision.

The correct contrast ratios allow a user to distinguish text on a background, and with these new colors we’re able to use a higher ratio for better separation between elements.

To calculate a contrast ratio, we take the designed assets and enter them into color analyzing tools. Some websites our designers use for calculating the contrast ratio for your images can be found here:

Contrast ratios 4.5:1 and above are required by the Web Content Accessibility Guidelines 2.0 Level AA; we try to target a contrast ratio of 7:1, which meets Level AAA standards for normal text.

For example, timestamps in messages used to use #ACADB1, which had a contrast ratio of 2.2:1 when compared to the white background. We changed it to #717274, which has a much higher ratio of 4.8:1.

Message Input

If you look at the before and after of the Message Input above (as well as the Channel Header at the beginning of this section), you can see the improvement in readability of the placeholder text: “Message #random” is almost totally transparent before, but after our changes the “+” matches the input’s borders and is more uniform with the rest of the Message Input element. The emoji button is also easier on the eye, and you can definitely tell you’re tapping a button instead of a smiling ghost on the screen. Rather than looking like several different components of different colors, the simplified palette makes the Message Input a singular object that has an improved contrast to the background.

The design team here at Slack has a huge impact on how we develop with accessibility in mind, and we are thankful for their insight to make sure we can reach these standards.

Testing

Accessibility elements have a bonus value for us as they’re great for testing. Since we can simulate different flows such as message posting with our end-to-end tests, we can use these identifiers to ensure each step is progressing as it should. Is the text in the message input matching the text that was posted? Can we identify the link a user should be able to tap on? Where is the username? We can use these elements because they change less often than other identifiers, and if a trait changes or text is in the wrong place, the tests will catch it instantly.

We use an automated testing library called Kif to implement our functional tests. It allows us to write UI tests that can be launched directly on the view controller without the overhead of having to log into the app, navigate to the view controller, and then the test. Usually functional UI tests are run in the UI test target, XCUITest. Unfortunately XCUITest comes with a lot of overhead so one of the reasons we chose Kif is because it’s built on top of XCTest, the unit testing target, which makes running tests much faster.

Here’s an example of a test that looks at the cells in our emoji picker view and ensures that the search bar and certain emojis are labeled correctly.

func testSearchingForEmoji() {
let emojiPicker = SLKEmojiPickerViewController(dependencies:    self.dependencies)
          self.setRootViewController(SLNavigationController(rootViewController:emojiPicker, dependencies:self.dependencies))
tester().enterText("heart", intoViewWithAccessibilityLabel: "Search")
tester().waitForView(withAccessibilityLabel:":heart:")
tester().waitForView(withAccessibilityLabel:":blue_heart:")
tester().waitForView(withAccessibilityLabel:":broken_heart:")
tester().waitForView(withAccessibilityLabel:":hearts:")
tester().tapView(withAccessibilityIdentifier: "Clear text")
tester().enterText("smiley", intoViewWithAccessibilityLabel: "Search")
tester().waitForView(withAccessibilityLabel:":smiley:")
tester().waitForView(withAccessibilityLabel:":smiley_cat:")
}

Takeaways

From the start of a project to feature complete, we make accessibility a key factor in our decision making. Promoting a culture of user diversity awareness helps foster creative and empathetic product thinking.

These enhancements don’t just benefit our users; they also assist our fellow coworkers and developers as accessibility standards set the bar for quality and maintainability within the user interface.

Apple integrates a great deal of tooling into their developer utilities that enables engineers to make sure their own products are compliant with accessibility standards. However, it’s up to the developer to expand functionality beyond these scopes if necessary, as we’ve done with tweaking font sizing and disabling animations across our clients.

Here is a list of some tools and tips on how to improve accessibility for your app:

  • Kif as an automated testing library
  • FLAnimatedImage for taking advantage of animated images and making them static
  • Colour Contrast Analyser and Color Oracle for testing the contrast of different elements within your app

At Slack, we continue to better understand how our users interact with our app in the way that best suits their abilities. If you’re interested in working with us on this, we would love to have you join!