diff --git a/mpd.tree b/mpd.tree index f2afd284..328f04cb 100644 --- a/mpd.tree +++ b/mpd.tree @@ -47,6 +47,7 @@ + @@ -69,7 +70,8 @@ - + + diff --git a/topics/compose/compose-accessibility.md b/topics/compose/compose-accessibility.md new file mode 100644 index 00000000..6be58033 --- /dev/null +++ b/topics/compose/compose-accessibility.md @@ -0,0 +1,109 @@ +[//]: # (title: Accessibility) + +Compose Multiplatform provides features essential for meeting accessibility standards, such as semantic properties, +accessibility APIs, and support for assistive technologies, including screen readers and keyboard navigation. + +The framework enables the design of applications that comply with the requirements of the +[European Accessibility Act](https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX%3A32019L0882) (EAA) +and the [Web Content Accessibility Guidelines](https://www.w3.org/TR/WCAG21/) (WCAG). + +## Semantic properties + +Semantic properties define the meaning and role of a component, adding context for services such as accessibility, +autofill, and testing. + +Semantic properties are the building blocks of the semantic tree. When you define semantic properties in composables, +they are automatically added to the semantic tree. When assistive technologies interact with the app, they traverse +the semantic tree, not the entire UI tree. + +Key semantic properties for accessibility: + +* `contentDescription` provides a description for non-textual or ambiguous UI elements like + [`IconButton`](https://kotlinlang.org/api/compose-multiplatform/material3/androidx.compose.material3/-icon-button.html), + and [`FloatingActionButton`](https://kotlinlang.org/api/compose-multiplatform/material3/androidx.compose.material3/-floating-action-button.html). + It is the primary accessibility API and serves for providing a textual label that screen readers announce. + + ```kotlin + Modifier.semantics { contentDescription = "Description of the image" } + ``` + +* `role` informs accessibility services about the UI component's functional type, such as button, + checkbox, or image. This helps screen readers interpret interaction models and announce the element properly. + + ```kotlin + Modifier.semantics { role = Role.Button } + ``` + +* `stateDescription` describes the current state of an interactive UI element. + + ```kotlin + Modifier.semantics { stateDescription = if (isChecked) "Checked" else "Unchecked" } + ``` + +* `testTag` is used primarily in UI testing via accessibility identifiers, like in the Espresso + framework on Android or XCUITest on iOS. In addition, `testTag` can be useful for debugging or in specific + automation scenarios where a component identifier is required. + + ```kotlin + Modifier.testTag("my_unique_element_id") + ``` + +For a full list of semantics properties, see the +[SemanticsProperties API](https://developer.android.com/reference/kotlin/androidx/compose/ui/semantics/SemanticsProperties) +reference. + +## Traversal order + +By default, screen readers navigate through UI elements in a fixed order, following their layout from left +to right and top to bottom. However, for complex layouts, screen readers may not automatically determine the correct +reading order. This is crucial for layouts with container views, +such as tables and nested views, that support the scrolling and zooming of contained views. + +To ensure the correct reading order when scrolling and swiping through complex views, define traversal semantic properties. +This also ensures correct navigation between different traversal groups with the swipe-up +or swipe-down accessibility gestures. + +The default value of the traversal index is `0f`. +The lower the traversal index value of a component, the earlier its content description will be read relative +to other components. + +For example, if you want the screen reader to prioritize a floating action button, +you can set its traversal index to `-1f`: + +```kotlin +@Composable +fun FloatingBox() { + Box( + modifier = + Modifier.semantics { + isTraversalGroup = true + // Sets a negative index to prioritize over elements with the default index + traversalIndex = -1f + } + ) { + FloatingActionButton(onClick = {}) { + Icon( + imageVector = Icons.Default.Add, + contentDescription = "Icon of floating action button" + ) + } + } +} +``` + +## What’s next + +Learn more about accessibility features for iOS: + +* [High-contrast theme](compose-ios-accessibility.md#high-contrast-theme) +* [Test accessibility with XCTest framework](compose-ios-accessibility.md#test-accessibility-with-xctest-framework) +* [Control via trackpad and keyboard](compose-ios-accessibility.md#control-via-trackpad-and-keyboard) +* [Synchronize the semantic tree with the iOS accessibility tree](compose-ios-accessibility.md#choose-the-tree-synchronization-option) + (for Compose Multiplatform 1.7.3 and earlier) + +Learn more about accessibility features for desktop: + +* [Enable accessibility on Windows](compose-desktop-accessibility.md#enabling-accessibility-on-windows) +* [Test your app with macOS and Windows tools](compose-desktop-accessibility.md#example-custom-button-with-semantic-rules) + +For implementation details, see the [Jetpack Compose documentation](https://developer.android.com/develop/ui/compose/accessibility). \ No newline at end of file diff --git a/topics/compose/compose-rtl.md b/topics/compose/compose-rtl.md index 8a17e03b..c86bf0f1 100644 --- a/topics/compose/compose-rtl.md +++ b/topics/compose/compose-rtl.md @@ -294,9 +294,7 @@ You can also define traversal semantics to ensure correct navigation between dif with the swipe-up or swipe-down accessibility gestures. For details on how to define traversal semantics and set traversal indexes, -refer to the [Accessibility](whats-new-compose-180.md#accessibility-for-container-views) section. - -[//]: # (todo: replace accessibility link) +refer to the [Accessibility](compose-accessibility.md#traversal-order) section. ## Known issues diff --git a/topics/compose/ios/compose-ios-accessibility.md b/topics/compose/ios/compose-ios-accessibility.md index abdae68d..eff1eb56 100644 --- a/topics/compose/ios/compose-ios-accessibility.md +++ b/topics/compose/ios/compose-ios-accessibility.md @@ -2,6 +2,7 @@ Compose Multiplatform accessibility support allows people with disabilities to interact with the Compose Multiplatform UI as comfortably as with the native iOS UI: + * Screen readers and VoiceOver can access the content of the Compose Multiplatform UI. * The Compose Multiplatform UI supports the same gestures as the native iOS UI for navigation and interaction. @@ -10,12 +11,131 @@ that are consumed by iOS Accessibility Services. For most interfaces built with automatically. You can also use this semantic data in testing and other automation: properties such as `testTag` will correctly map -to native accessibility properties such as `accessibilityIdentifier`. This makes semantic data from Compose Multiplatform available -to Accessibility Services and XCTest framework. +to native accessibility properties such as `accessibilityIdentifier`. This makes semantic data from Compose Multiplatform +available to Accessibility Services and XCTest framework. + +## High-contrast theme + +Compose Multiplatform uses the [`ColorScheme`](https://kotlinlang.org/api/compose-multiplatform/material3/androidx.compose.material3/-color-scheme/) +class from the material3 library, which currently lacks out-of-the-box support for high-contrast colours. +For a high-contrast theme on iOS, you need to add an extra set of colours +to the application palette. For each custom colour, its high-contrast version should be specified manually. + +When you provide an additional high-contrast color palette, users need to manually select it, as it will not be +automatically triggered by iOS's system-level accessibility settings. + +While defining a color palette, use a WCAG-compliant contrast checker tool to verify that your chosen `onPrimary` color has +sufficient contrast with your primary color, `onSurface` with a surface color, and so on. +Ensure the contrast ratio between colors is at least 4.5:1. For custom foreground and background colors, +the contrast ratio should be 7:1, especially for small text. This applies to both your `lightColorScheme` +and `darkColorScheme`. + +This code shows how to define high-contrast color palettes for light and dark themes: + +```kotlin +import androidx.compose.ui.graphics.Color + +// Defines a data class to hold the color palette for high-contrast themes +data class HighContrastColors( + val primary: Color, // Main interactive elements, primary text, top app bars + val onPrimary: Color, // Content displayed on top of a 'primary' color + val secondary: Color, // Secondary interactive elements, floating action buttons + val onSecondary: Color, // Content displayed on top of a 'secondary' color + val tertiary: Color, // An optional third accent color + val onTertiary: Color, // Content displayed on top of a 'tertiary' color + val background: Color, // Main background of the screen + val onBackground: Color, // Content displayed on top of a 'background' color + val surface: Color, // Card backgrounds, sheets, menus, elevated surfaces + val onSurface: Color, // Content displayed on top of a 'surface' color + val error: Color, // Error states and messages + val onError: Color, // Content displayed on top of an 'error' color + val success: Color, // Success states and messages + val onSuccess: Color, // Content displayed on top of a 'success' color + val warning: Color, // Warning states and messages + val onWarning: Color, // Content displayed on top of a 'warning' color + val outline: Color, // Borders, dividers, disabled states + val scrim: Color // Dimming background content behind modals/sheets +) + +// Neutral colors +val Black = Color(0xFF000000) +val White = Color(0xFFFFFFFF) +val DarkGrey = Color(0xFF1A1A1A) +val MediumGrey = Color(0xFF888888) +val LightGrey = Color(0xFFE5E5E5) + +// Primary accent colors +val RoyalBlue = Color(0xFF0056B3) +val SkyBlue = Color(0xFF007AFF) + +// Secondary and tertiary accent colors +val EmeraldGreen = Color(0xFF28A745) +val GoldenYellow = Color(0xFFFFC107) +val DeepPurple = Color(0xFF6F42C1) + +// Status colors +val ErrorRed = Color(0xFFD32F2F) +val SuccessGreen = Color(0xFF388E3C) +val WarningOrange = Color(0xFFF57C00) + +// Light high-contrast palette, dark content on light backgrounds +val LightHighContrastPalette = + HighContrastColors( + primary = RoyalBlue, + onPrimary = White, + secondary = EmeraldGreen, + onSecondary = White, + tertiary = DeepPurple, + onTertiary = White, + background = White, + onBackground = Black, + surface = LightGrey, + onSurface = DarkGrey, + error = ErrorRed, + onError = White, + success = SuccessGreen, + onSuccess = White, + warning = WarningOrange, + onWarning = White, + outline = MediumGrey, + scrim = Black.copy(alpha = 0.6f) + ) + +// Dark high-contrast palette, light content on dark backgrounds +val DarkHighContrastPalette = + HighContrastColors( + primary = SkyBlue, + onPrimary = Black, + secondary = EmeraldGreen, + onSecondary = White, + tertiary = GoldenYellow, + onTertiary = Black, + background = Black, + onBackground = White, + surface = DarkGrey, + onSurface = LightGrey, + error = ErrorRed, + onError = White, + success = SuccessGreen, + onSuccess = White, + warning = WarningOrange, + onWarning = White, + outline = MediumGrey, + scrim = Black.copy(alpha = 0.6f) + ) +``` +{initial-collapse-state="collapsed" collapsible="true" collapsed-title="val LightHighContrastPalette = HighContrastColors( primary = RoyalBlue,"} -iOS accessibility support is in the early stages of development. If you have trouble with this feature, -we would appreciate your feedback in the [#compose-ios](https://kotlinlang.slack.com/archives/C0346LWVBJ4/p1678888063176359) -Slack channel or as an issue in [YouTrack](https://youtrack.jetbrains.com/newIssue?project=CMP). +## Control via trackpad and keyboard + +Compose Multiplatform for iOS supports additional input methods to control your device. Instead of relying on the +touchscreen, you can enable either AssistiveTouch to use a mouse or trackpad, or Full Keyboard Access to use a keyboard: + +* AssistiveTouch (**Settings** | **Accessibility** | **Touch** | **AssistiveTouch**) allows you to control your + iPhone or iPad with a pointer from a connected mouse or trackpad. You can use the pointer to click icons on your + screen, navigate through the AssistiveTouch menu, or type using the onscreen keyboard. +* Full Keyboard Access (**Settings** | **Accessibility** | **Keyboards** | **Full Keyboard Access**) enables device + control with a connected keyboard. You can navigate with keys like **Tab** and activate items using **Space**. ## Customize synchronization of the accessibility tree @@ -94,9 +214,28 @@ ComposeUIViewController(configure = { } ``` +## Test accessibility with XCTest framework + +You can use the semantic accessibility data in testing and other automation. Properties such as `testTag` correctly map +to native accessibility properties such as `accessibilityIdentifier`. This makes semantic data from Compose Multiplatform +available to Accessibility Services and the XCTest framework. + +You can use automated accessibility audits in your UI tests. +Calling `performAccessibilityAudit()` for your `XCUIApplication` will audit the current view for accessibility +issues just as the Accessibility Inspector does. + +```swift +func testAccessibilityTabView() throws { + let app = XCUIApplication() + app.launch() + app.tabBars.buttons["MyLabel"].tap() + + try app.performAccessibilityAudit() +} +``` + ## What's next? -Now that you're up to speed with iOS accessibility support in Compose Multiplatform: -* Try out the project generated by [the Kotlin Multiplatform wizard](https://kmp.jetbrains.com/) in your usual iOS accessibility workflow. -* Learn about [resource qualifiers](compose-multiplatform-resources.md) that will be helpful when adapting -a Compose Multiplatform UI to a particular situation. \ No newline at end of file +* Learn more in the [Apple accessibility](https://developer.apple.com/accessibility/) guide. +* Try out the project generated by [the Kotlin Multiplatform wizard](https://kmp.jetbrains.com/) in your usual + iOS accessibility workflow. \ No newline at end of file diff --git a/topics/whats-new/whats-new-compose-180.md b/topics/whats-new/whats-new-compose-180.md index b4bb98dd..b82dcad4 100644 --- a/topics/whats-new/whats-new-compose-180.md +++ b/topics/whats-new/whats-new-compose-180.md @@ -271,47 +271,16 @@ Localized versions of these announcements are also provided, allowing VoiceOver #### Accessibility for container views -By default, screen readers navigate through UI elements in a fixed order, -following their layout from left to right and top to bottom. -For complex layouts, -screen readers cannot determine the correct reading order without properly defined traversal semantics. -This is particularly relevant for layouts with container views, such as tables and nested views, -that support the scrolling and zooming of contained views. - Starting with Compose Multiplatform %composeVersion%, you can define traversal semantic properties for containers to ensure the correct reading order when scrolling and swiping through complex views. -For example, if you want the screen reader to first focus on a floating action button, -you can set its traversal index to `-1f`. -The default value of the index is `0f`, -which means that the lower specified value causes the element to be read before others on the screen. - -```kotlin -@Composable -fun FloatingBox() { - Box( - modifier = - Modifier.semantics { - isTraversalGroup = true - // Sets a negative index to prioritize over elements with the default index - traversalIndex = -1f - } - ) { - FloatingActionButton(onClick = {}) { - Icon( - imageVector = Icons.Default.Add, - contentDescription = "Icon of floating action button" - ) - } - } -} -``` - In addition to proper sorting elements for screen readers, support for traversal properties enables navigation between different traversal groups with the swipe-up or swipe-down accessibility gestures. To switch to accessible navigation mode for containers, rotate two fingers on the screen while VoiceOver is active. +Learn more about traversal semantic properties in the [Accessibility](compose-accessibility.md#traversal-order) section. + #### Accessible text input In Compose Multiplatform %composeVersion% we've introduced support for text fields' accessibility traits. @@ -321,6 +290,7 @@ ensuring proper accessibility-state representation. You can now also use accessible text input in UI testing. #### Support for control via trackpad and keyboard + Compose Multiplatform for iOS now supports two additional input methods to control your device. Instead of relying on the touchscreen, you can enable either AssistiveTouch to use a mouse or trackpad, or Full Keyboard Access to use a keyboard: