--- comment: | This is an auto-generated file. DO NOT EDIT THIS FILE DIRECTLY. To edit this file, modify the source file in the 'mobile/overview/' directory, then run the 'copy-abyss-info' script to regenerate this file. id: abyss-overview title: Abyss Overview hide_table_of_contents: true --- --- comment: | This is an auto-generated file. DO NOT EDIT THIS FILE DIRECTLY. To edit this file, modify the source file in the 'mobile/overview/' directory, then run the 'copy-abyss-info' script to regenerate this file. id: about title: About Abyss --- ## What is Abyss? ## How Abyss works ## We Support Adoption ## Guiding Principles ## We Maintain Assets ## The Abyss Team --- comment: | This is an auto-generated file. DO NOT EDIT THIS FILE DIRECTLY. To edit this file, modify the source file in the 'mobile/overview/' directory, then run the 'copy-abyss-info' script to regenerate this file. id: abyss-version-2 title: Abyss Version 2 hide_table_of_contents: true ---
## Abyss Design System version 2 ## V2 prep for designers ## V2 prep for developers ## Stay Connected --- comment: | This is an auto-generated file. DO NOT EDIT THIS FILE DIRECTLY. To edit this file, modify the source file in the 'mobile/overview/' directory, then run the 'copy-abyss-info' script to regenerate this file. id: releases title: Releases hide_table_of_contents: true ---
--- comment: | This is an auto-generated file. DO NOT EDIT THIS FILE DIRECTLY. To edit this file, modify the source file in the 'mobile/overview/' directory, then run the 'copy-abyss-info' script to regenerate this file. id: contact-us title: Contact Us hide_table_of_contents: true --- ## Support ## Requests --- comment: | This is an auto-generated file. DO NOT EDIT THIS FILE DIRECTLY. To edit this file, modify the source file in the 'mobile/overview/' directory, then run the 'copy-abyss-info' script to regenerate this file. id: disclaimer title: Mobile Components Disclaimer --- As a note, these components were developed for use in a mobile application environment. For your convenience, we created documentation and interactive examples on our Abyss site. We have tried our best to accurately show the functionality and style of each component on these pages, but you may notice some differences and limitations as the optimal platform for viewing and interacting with these components is a mobile device. A few examples are described below: - [Date Input](/mobile/ui/date-input) requires native code, so this component is only supported on iOS and Android. There are no interactive examples in the documentation; only recordings of each feature are available. To fully interact with the date and time pickers you will need to import the component within a mobile development environment. - [Modal](/mobile/ui/modal), and any component that uses Modal, takes up the entire browser window when opened. While it is intended to take up the entire phone screen as well, the content on the modal will fill much more space when used on a mobile device than it does in the browser window. --- id: brandmark category: Brand title: Brandmark description: Logos/Brandmarks for Optum brands. design: https://www.figma.com/design/AxPl4wZUpCl5Wl5yjrPUG8?node-id=75764-3185 --- ```jsx import { Brandmark } from '@uhg-abyss/mobile'; ``` ```tsx example () => { return ( ); }; ``` ## Brand Use the `brand` property to adjust which brand is being selected. ```tsx example () => { return ( ); }; ``` ## Size Use the `size` property to adjust the size of the brandmark. ```tsx example () => { return ( ); }; ``` ## Affiliate Use the `affiliate` property to select the required brandmark affiliates. ```tsx example () => { return ( ); }; ``` ## Variant Use the `variant` property to select the required brandmark variants. ```tsx example () => { return ( ); }; ``` ## Color Use the `color` property to select available brandmark colors. ```tsx example () => { return ( ); }; ``` ### Brandmark Props ## Brandmark Props | Prop | Type | Description | Default | Required | |------|------|-------------|---------|----------| | `affiliate` | `'aarp_extra_assurance_benefits' \| 'aarp_medicare_plans' \| 'aarp_medicare_prescription' \| 'aarp_medicare_advantage' \| 'aarp_medicare_supplement' \| 'aarp_supplemental_personal_health' \| 'community_plan' \| 'dental' \| 'dual_complete' \| 'global' \| 'hearing' \| 'medicare_advantage' \| 'medicare_plans' \| 'medicare_solutions' \| 'oxford' \| 'uhc' \| 'vision' \| 'optum' \| 'optum_financial' \| 'optum_frontier_therapies' \| 'optum_perks' \| 'optum_prescription' \| 'optum_serve' \| 'optum_store' \| 'uhg' \| 'surest'` | Indicates the brandmark affiliate | `-` | Yes | | `brand` | `'uhc' \| 'optum' \| 'uhg' \| 'surest' \| undefined` | Indicates which brand you want your brandmark from | `-` | No | | `color` | `string \| undefined` | Set color option of the brandmark | `-` | No | | `onError` | `() => void \| undefined` | Callback function when the brandmark has an error loading | `-` | No | | `onLoad` | `() => void \| undefined` | Callback function when the brandmark is loaded | `-` | No | | `size` | `'$sm' \| '$md' \| '$lg' \| number \| undefined` | Set size of brandmark | `200` | No | | `sizes` | `Record \| undefined` | Pre programmed sizes for the brandmark | `'{ sm: 100, md: 150, lg: 200 }'` | No | | `title` | `string \| undefined` | Set title for the brandmark | `-` | No | | `variant` | `'lockup' \| 'lockup_horizontal' \| 'u_mark' \| 'u_mark_horizontal' \| 'monogram' \| 'stacked_wordmark' \| 'wordmark' \| 'lockup' \| 'lockup' \| 's_mark'` | Indicates the brandmark variant | `-` | Yes | ### Brandmark Classes ```tsx example () => { return

Brandmarks

; }; ```
The source for these brandmarks can be found in the [Brandmark Library](https://brand.optum.com/content/wordmark-library-resources). You can use the search functionality to find the required brandmark. Brandmarks can be searched using their affiliates, variants or colors.
### Brandmark Tokens
--- id: icon-brand category: Brand title: IconBrand description: Used to implement Brand icons and adapt their properties. design: https://www.figma.com/design/anZoHg026SyKJHWGJ7Vf4Q?node-id=1-431 --- ```jsx import { IconBrand } from '@uhg-abyss/mobile'; ``` ```tsx example () => { return ; }; ``` ## Icons Use the `icon` property to adjust which icon is being selected. ```tsx example () => { return ( ); }; ``` ## Size Use the `size` property to adjust the size of an icon by setting it to a specific number, or using a token. The default is `$md`. Token sizes: `$icon-brand.sizing.xs`: 40 `$icon-brand.sizing.sm`: 64 `$icon-brand.sizing.md`: 96 `$icon-brand.sizing.lg`: 112 `$icon-brand.sizing.xl`: 136 ```tsx example () => { return ( ); }; ``` ## Brand Icon Variants Use the `variant` property to change the style of Brand icons. Available variants are `twotonedarkcircle`, `twotonelightcircle`, `twotone`, `onetonedarkcircle`, and `onetone`. The default variant is `twotonedarkcircle`. ```tsx example () => { return ( onetonedarkcircle twotonedarkcircle twotonelightcircle onetone twotone ); }; ``` ### IconBrand Props ## IconBrand Props | Prop | Type | Description | Default | Required | |------|------|-------------|---------|----------| | `brand` | `'uhc' \| 'optum' \| 'uhg' \| 'surest' \| undefined` | Indicates which brand you want your icon from. This changes the colors of the icon to match the brand you select | `-` | No | | `delayLongPress` | `number \| undefined` | Delay in milliseconds before the `onLongPress` callback is triggered. | `-` | No | | `delayPressIn` | `number \| undefined` | Delay in milliseconds before the `onPressIn` callback is triggered. | `-` | No | | `delayPressOut` | `number \| undefined` | Delay in milliseconds before the `onPressOut` callback is triggered. | `-` | No | | `disabled` | `boolean \| undefined` | Disables press interactions for the icon. | `-` | No | | `disableScaling` | `boolean \| undefined` | Specifies whether the icon should scale to respect Text Size accessibility settings | `false` | No | | `fallback` | `React.JSX.Element \| undefined` | Element to render if the icon fails to load | `-` | No | | `icon` | `ValidIconBrandName` | Name of the brand icon | `-` | Yes | | `isScreenReadable` | `boolean \| undefined` | Indicate whether the icon is screen readable or not. If the icon is screen readable, then provide a title | `-` | No | | `maxFontSizeMultiplier` | `number \| undefined` | Specifies the largest possible scale the icon can reach when `disableScaling` is false | `-` | No | | `onError` | `() => void \| undefined` | Callback function to be called when the icon fails to load | `-` | No | | `onLoad` | `() => void \| undefined` | Callback function to be called when the icon is loaded | `-` | No | | `onLongPress` | `(event: GestureResponderEvent) => void \| undefined` | Callback fired after a long press gesture is detected. | `-` | No | | `onPress` | `(event: GestureResponderEvent) => void \| undefined` | Callback fired when the icon is pressed. | `-` | No | | `onPressIn` | `(event: GestureResponderEvent) => void \| undefined` | Callback fired when a press-in gesture starts. | `-` | No | | `onPressOut` | `(event: GestureResponderEvent) => void \| undefined` | Callback fired when a press-in gesture ends. | `-` | No | | `size` | `Abyss.Size \| undefined` | Set the size of the icon | `'$icon-brand.sizing.md'` | No | | `sizes` | `Record \| undefined` | Pre programmed sizes for the icons | `-` | No | | `title` | `string \| undefined` | Set the title of the icon | `-` | No | | `variant` | `'onetone' \| 'onetonedarkcircle' \| 'twotone' \| 'twotonelightcircle' \| 'twotonedarkcircle' \| undefined` | The style/color variation of the brand icon | `'twotonedarkcircle'` | No | ### IconBrand Classes ```tsx example () => { return

Brand Icons

; }; ```
Abyss uses branded iconography iconography that is designed to aid wayfinding, draw attention, and support messaging. The source for these design icons can be found in the [Brand Icons Library](https://brand.uhc.com/content/iconography).
### IconBrand Tokens
--- id: icon-custom category: Brand title: IconCustom description: Used to implement custom icons and adapt their properties. design: https://www.figma.com/design/anZoHg026SyKJHWGJ7Vf4Q?node-id=5-159 --- ```jsx import { IconCustom } from '@uhg-abyss/mobile'; ``` ```tsx example () => { return ; }; ``` ## Size Use the `size` property to adjust the size of an icon by setting it to a specific number or token. The default size is set to 24. Token sizes: `$xs`: 16 `$sm`: 20 `$md`: 24 `$lg`: 40 `$xl`: 48 ```tsx example () => { return ( ); }; ``` ## Variants Use the `variant` prop to change the style of the custom icons. The prop takes in either a `light` or `lightactive` value. The default is `light`. ```tsx example () => { return ( light lightactive ); }; ``` ## Brand Use the `brand` property to adjust which brands icons are being selected. Note that some of the icons are unique to certain brands. The default is the theme brand, then falls back to `uhc`. ```tsx example () => { return ( ); }; ``` ## Active When placing an icon inside of a clickable element, the icon should be toggled between its normal and active state. ```tsx example () => { return ( {({ pressed }) => { return ( Home ); }} ); }; ``` ## Indicator The [indicator component](/mobile/ui/indicator) can be used as a wrapper to add a notification badge. ```tsx example () => { return ( ); }; ``` ### IconCustom Props ## IconCustom Props | Prop | Type | Description | Default | Required | |------|------|-------------|---------|----------| | `brand` | `'uhc' \| 'optum' \| undefined` | Specifies the brand of the custom icon. Defaults to the theme's brand, then falls back to 'uhc'. | `-` | No | | `icon` | `keyof typeof dark \| keyof typeof light` | Name of the custom icon | `-` | Yes | | `variant` | `'light' \| 'dark' \| 'darkactive' \| 'lightactive' \| 'light' \| 'lightactive' \| undefined` | The style variation of the custom icon | `'light'` | No | ### IconCustom Classes ```tsx example () => { return

Custom Icons

; }; ```
### IconCustom Tokens
--- id: illustration-brand category: Media title: IllustrationBrand description: Used to implement Brand illustrations and adapt their properties. design: https://www.figma.com/design/34gRQMq2NxFgeRynjqd24C?node-id=1218-19509 --- ```jsx import { IllustrationBrand } from '@uhg-abyss/mobile'; ``` ```tsx example () => { return ( ); }; ``` ## Brand Use the `brand` property to adjust which brand is being selected. ```tsx example () => { return ; }; ``` ## Size Use the `size` property to adjust the size of the illustration. `IllustrationBrand` scales proportionally based on the image ratio and renders with a contain resize mode so the full illustration stays visible. If you wrap it, prefer a layout that lets the illustration grow from its width instead of a fixed-height container with clipping styles. ```tsx example () => { return ( ); }; ``` ### IllustrationBrand Props ## IllustrationBrand Props | Prop | Type | Description | Default | Required | |------|------|-------------|---------|----------| | `brand` | `'uhc' \| 'optum' \| 'surest' \| undefined` | Indicates which brand you want your illustration from | `-` | No | | `color` | `IllustrationColor \| undefined` | Sets color option of the illustration | `-` | No | | `illustration` | `UhcIllustrationBrandName \| undefined \| OptumIllustrationBrandName \| SurestIllustrationBrandName` | The name of the illustration | `-` | No | | `size` | `Abyss.Size \| undefined` | Sets the size of the illustration | `200` | No | | `title` | `string \| undefined` | Set accessibilityLabel of the image | `-` | No | | `variant` | `IllustrationVariant \| undefined` | The color variant for illustrations with multiple variants on the same background color. Applicable only to certain UHC illustrations | `1` | No | ### IllustrationBrand Classes ```tsx example () => { return

Illustration Source

; }; ```
The source for these illustrations can be found in the brand libraries.

[UnitedHealthCare Library](https://unitedhealthcare.gettyimages.com/s/3f79xfhwgtcsc8t3t79hfc9)
[Optum Library](https://brand.optum.com/content/illustration-library-resources)

You can use the search functionality to find the required illustration. Illustrations can be searched using their title, variants, or colors.

--- id: brandmark category: Brand title: Brandmark description: Logos/Brandmarks for UHC brands. design: https://www.figma.com/design/wCMblLsq9TxAQvKzY3EfCt?node-id=82983-2524 --- ```jsx import { Brandmark } from '@uhg-abyss/mobile'; ``` ```tsx example () => { return ( ); }; ``` ## Brand Use the `brand` property to adjust which brand is being selected. ```tsx example () => { return ( ); }; ``` ## Size Use the `size` property to adjust the size of the brandmark. ```tsx example () => { return ( ); }; ``` ## Affiliate Use the `affiliate` property to select the required brandmark affiliates. ```tsx example () => { return ( ); }; ``` ## Variant Use the `variant` property to select the required brandmark variants. ```tsx example () => { return ( ); }; ``` ## Color Use the `color` property to select available brandmark colors. ```tsx example () => { return ( ); }; ``` ### Brandmark Props ## Brandmark Props | Prop | Type | Description | Default | Required | |------|------|-------------|---------|----------| | `affiliate` | `'aarp_extra_assurance_benefits' \| 'aarp_medicare_plans' \| 'aarp_medicare_prescription' \| 'aarp_medicare_advantage' \| 'aarp_medicare_supplement' \| 'aarp_supplemental_personal_health' \| 'community_plan' \| 'dental' \| 'dual_complete' \| 'global' \| 'hearing' \| 'medicare_advantage' \| 'medicare_plans' \| 'medicare_solutions' \| 'oxford' \| 'uhc' \| 'vision' \| 'optum' \| 'optum_financial' \| 'optum_frontier_therapies' \| 'optum_perks' \| 'optum_prescription' \| 'optum_serve' \| 'optum_store' \| 'uhg' \| 'surest'` | Indicates the brandmark affiliate | `-` | Yes | | `brand` | `'uhc' \| 'optum' \| 'uhg' \| 'surest' \| undefined` | Indicates which brand you want your brandmark from | `-` | No | | `color` | `string \| undefined` | Set color option of the brandmark | `-` | No | | `onError` | `() => void \| undefined` | Callback function when the brandmark has an error loading | `-` | No | | `onLoad` | `() => void \| undefined` | Callback function when the brandmark is loaded | `-` | No | | `size` | `'$sm' \| '$md' \| '$lg' \| number \| undefined` | Set size of brandmark | `200` | No | | `sizes` | `Record \| undefined` | Pre programmed sizes for the brandmark | `'{ sm: 100, md: 150, lg: 200 }'` | No | | `title` | `string \| undefined` | Set title for the brandmark | `-` | No | | `variant` | `'lockup' \| 'lockup_horizontal' \| 'u_mark' \| 'u_mark_horizontal' \| 'monogram' \| 'stacked_wordmark' \| 'wordmark' \| 'lockup' \| 'lockup' \| 's_mark'` | Indicates the brandmark variant | `-` | Yes | ### Brandmark Classes ```tsx example () => { return

Brandmarks

; }; ```
The source for these brandmarks can be found in the [Brandmark Library](https://brand.uhc.com/content/logo-brandmark-library). You can use the search functionality to find the required brandmark. Brandmarks can be searched using their affiliates, variants or colors.
### Brandmark Tokens
--- id: icon-brand category: Brand title: IconBrand description: Used to implement Brand icons and adapt their properties. design: https://www.figma.com/design/anZoHg026SyKJHWGJ7Vf4Q?node-id=1-431 --- ```jsx import { IconBrand } from '@uhg-abyss/mobile'; ``` ```tsx example () => { return ; }; ``` ## Icons Use the `icon` property to adjust which icon is being selected. ```tsx example () => { return ( ); }; ``` ## Size Use the `size` property to adjust the size of an icon by setting it to a specific number, or using a token. The default is `$md`. Token sizes: `$icon-brand.sizing.xs`: 40 `$icon-brand.sizing.sm`: 64 `$icon-brand.sizing.md`: 96 `$icon-brand.sizing.lg`: 112 `$icon-brand.sizing.xl`: 136 ```tsx example () => { return ( ); }; ``` ## Brand Icon Variants Use the `variant` property to change the style of Brand icons. Available variants are `twotonedarkcircle`, `twotonelightcircle`, `twotone`, `onetonedarkcircle`, and `onetone`. The default variant is `twotonedarkcircle`. ```tsx example () => { return ( onetonedarkcircle twotonedarkcircle twotonelightcircle onetone twotone ); }; ``` ### IconBrand Props ## IconBrand Props | Prop | Type | Description | Default | Required | |------|------|-------------|---------|----------| | `brand` | `'uhc' \| 'optum' \| 'uhg' \| 'surest' \| undefined` | Indicates which brand you want your icon from. This changes the colors of the icon to match the brand you select | `-` | No | | `delayLongPress` | `number \| undefined` | Delay in milliseconds before the `onLongPress` callback is triggered. | `-` | No | | `delayPressIn` | `number \| undefined` | Delay in milliseconds before the `onPressIn` callback is triggered. | `-` | No | | `delayPressOut` | `number \| undefined` | Delay in milliseconds before the `onPressOut` callback is triggered. | `-` | No | | `disabled` | `boolean \| undefined` | Disables press interactions for the icon. | `-` | No | | `disableScaling` | `boolean \| undefined` | Specifies whether the icon should scale to respect Text Size accessibility settings | `false` | No | | `fallback` | `React.JSX.Element \| undefined` | Element to render if the icon fails to load | `-` | No | | `icon` | `ValidIconBrandName` | Name of the brand icon | `-` | Yes | | `isScreenReadable` | `boolean \| undefined` | Indicate whether the icon is screen readable or not. If the icon is screen readable, then provide a title | `-` | No | | `maxFontSizeMultiplier` | `number \| undefined` | Specifies the largest possible scale the icon can reach when `disableScaling` is false | `-` | No | | `onError` | `() => void \| undefined` | Callback function to be called when the icon fails to load | `-` | No | | `onLoad` | `() => void \| undefined` | Callback function to be called when the icon is loaded | `-` | No | | `onLongPress` | `(event: GestureResponderEvent) => void \| undefined` | Callback fired after a long press gesture is detected. | `-` | No | | `onPress` | `(event: GestureResponderEvent) => void \| undefined` | Callback fired when the icon is pressed. | `-` | No | | `onPressIn` | `(event: GestureResponderEvent) => void \| undefined` | Callback fired when a press-in gesture starts. | `-` | No | | `onPressOut` | `(event: GestureResponderEvent) => void \| undefined` | Callback fired when a press-in gesture ends. | `-` | No | | `size` | `Abyss.Size \| undefined` | Set the size of the icon | `'$icon-brand.sizing.md'` | No | | `sizes` | `Record \| undefined` | Pre programmed sizes for the icons | `-` | No | | `title` | `string \| undefined` | Set the title of the icon | `-` | No | | `variant` | `'onetone' \| 'onetonedarkcircle' \| 'twotone' \| 'twotonelightcircle' \| 'twotonedarkcircle' \| undefined` | The style/color variation of the brand icon | `'twotonedarkcircle'` | No | ### IconBrand Classes ```tsx example () => { return

Brand Icons

; }; ```
Abyss uses branded iconography that is designed to aid way-finding, draw attention, and support messaging. The source for these design icons can be found in the [Brand Icons Library](https://brand.uhc.com/content/iconography).
## Dynamic Type Brand icons do not scale. When using IconBrand, the `disabledScaling` prop should be set to `true`. ### IconBrand Tokens
--- id: icon-custom category: Brand title: IconCustom description: Used to implement custom icons and adapt their properties. design: https://www.figma.com/design/anZoHg026SyKJHWGJ7Vf4Q?node-id=5-159 --- ```jsx import { IconCustom } from '@uhg-abyss/mobile'; ``` ```tsx example () => { return ; }; ``` ## Size Use the `size` property to adjust the size of an icon by setting it to a specific number or token. The default size is set to 24. Token sizes: `$xs`: 16 `$sm`: 20 `$md`: 24 `$lg`: 40 `$xl`: 48 ```tsx example () => { return ( ); }; ``` ## Variants Use the `variant` prop to change the style of the custom icons. The prop takes in a `light` and `dark` value, as well as `lightactive` and `darkactive` counterparts. The default is `light`. ```tsx example () => { return ( light lightactive dark darkactive ); }; ``` ## Brand Use the `brand` property to adjust which brands icons are being selected. Note that some of the icons are unique to certain brands. The default is the theme brand, then falls back to `uhc`. ```tsx example () => { return ; }; ``` ## Active When placing an icon inside of a clickable element, the icon should be toggled between its normal and active state. ```tsx example () => { return ( {({ pressed }) => { return ( Home ); }} ); }; ``` ## Indicator The [indicator component](/mobile/ui/indicator) can be used as a wrapper to add a notification badge. ```tsx example () => { return ( ); }; ``` ### IconCustom Props ## IconCustom Props | Prop | Type | Description | Default | Required | |------|------|-------------|---------|----------| | `brand` | `'uhc' \| 'optum' \| undefined` | Specifies the brand of the custom icon. Defaults to the theme's brand, then falls back to 'uhc'. | `-` | No | | `icon` | `keyof typeof dark \| keyof typeof light` | Name of the custom icon | `-` | Yes | | `variant` | `'light' \| 'dark' \| 'darkactive' \| 'lightactive' \| 'light' \| 'lightactive' \| undefined` | The style variation of the custom icon | `'light'` | No | ### IconCustom Classes ```tsx example () => { return

Custom Icons

; }; ```
### IconCustom Tokens
--- id: illustrated-icon-brand slug: /mobile/brand/uhc/illustrated-icon-brand category: Brand title: IllustratedIconBrand description: Used to implement UHC brand illustrated icons and adapt their properties. sourceIsTS: true --- ```jsx import { IllustratedIconBrand } from '@uhg-abyss/mobile'; ``` ```tsx sandbox { component: 'IllustratedIconBrand', inputs: [ { prop: 'icon', type: 'string', }, { prop: 'size', type: 'string', }, { prop: 'color', type: 'select', options: [ { label: 'none', value: undefined }, { label: 'gold', value: 'gold' }, { label: 'orange', value: 'orange' }, { label: 'multicolor', value: 'multicolor' }, ] }, ], } // Disclaimer: Not all icon/color combinations are applicable; inapplicable combinations will display as empty ``` ## Size Use the `size` property to adjust the width of the illustrated icon. The default value is `100`. The height of the illustration will scale proportionally to the width. ```tsx live ``` ## Color Use the `color` property to select available illustrated icon colors. The available colors are `"gold"`, `"orange"`, and `"multicolor"`. :::warning Not all illustrated icons have any color variants. In such cases, omit the `color` prop; otherwise, the icon will not display. ::: ```tsx live ``` ### IllustratedIconBrand Props ## IllustratedIconBrand Props | Prop | Type | Description | Default | Required | |------|------|-------------|---------|----------| | `color` | `'gold' \| 'multicolor' \| 'orange' \| undefined` | The color of the illustrated icon. Applicable only to certain assets. | `-` | No | | `icon` | `keyof typeof uhc.gold \| keyof typeof uhc.multicolor \| keyof typeof uhc.orange \| keyof typeof uhc.none` | Name of the illustrated icon asset. | `-` | Yes | | `size` | `number \| undefined` | The width of the icon. | `100` | No | ### IllustratedIconBrand Classes ## Screen Reader Support Illustrated icons are intended to be used as [decorative images](https://www.w3.org/WAI/tutorials/images/decorative/) and as such, are ignored by screen readers by default. However, should a case arise in which an illustrated icon needs to be accessible, use the `accessibilityLabel` prop to provide accessible text to the image. This text should be descriptive enough to convey the meaning of the illustrated icon. ```tsx live ``` ```tsx example () => { return

Illustrated Icon Source

; }; ```
The source for these illustrated icons can be found in the brand libraries.

[UnitedHealthCare Library](https://unitedhealthcare.gettyimages.com/s/3f79xfhwgtcsc8t3t79hfc9)

You can use the search functionality to find the required illustrated icons. Icons can be searched using their title or colors.
--- id: illustration-brand category: Brand title: IllustrationBrand description: Used to implement Brand illustrations and adapt their properties. design: https://www.figma.com/design/34gRQMq2NxFgeRynjqd24C?node-id=1218-19509 --- ```jsx import { IllustrationBrand } from '@uhg-abyss/mobile'; ``` ```tsx example () => { return ( ); }; ``` ## Brand Use the `brand` property to adjust which brand is being selected. ```tsx example () => { return ( ); }; ``` ## Size Use the `size` property to adjust the size of the illustration. `IllustrationBrand` scales proportionally based on the image ratio and renders with a contain resize mode so the full illustration stays visible. If you wrap it, prefer a layout that lets the illustration grow from its width instead of a fixed-height container with clipping styles. ```tsx example () => { return ; }; ``` ## Color Use the `color` property to select available illustration colors. ```tsx example () => { return ( ); }; ``` ## Variant Some UHC illustrations have multiple variants of accent colors on the same background color. Use the `variant` prop to select the color combination. ```tsx example () => { return ( ); }; ``` ### IllustrationBrand Props ## IllustrationBrand Props | Prop | Type | Description | Default | Required | |------|------|-------------|---------|----------| | `brand` | `'uhc' \| 'optum' \| 'surest' \| undefined` | Indicates which brand you want your illustration from | `-` | No | | `color` | `IllustrationColor \| undefined` | Sets color option of the illustration | `-` | No | | `illustration` | `UhcIllustrationBrandName \| undefined \| OptumIllustrationBrandName \| SurestIllustrationBrandName` | The name of the illustration | `-` | No | | `size` | `Abyss.Size \| undefined` | Sets the size of the illustration | `200` | No | | `title` | `string \| undefined` | Set accessibilityLabel of the image | `-` | No | | `variant` | `IllustrationVariant \| undefined` | The color variant for illustrations with multiple variants on the same background color. Applicable only to certain UHC illustrations | `1` | No | ### IllustrationBrand Classes ```tsx example () => { return

Illustration Source

; }; ```
The source for these illustrations can be found in the brand libraries.

[UnitedHealthCare Library](https://unitedhealthcare.gettyimages.com/s/3f79xfhwgtcsc8t3t79hfc9)
[Optum Library](https://brand.optum.com/content/illustration-library-resources)

You can use the search functionality to find the required illustration. Illustrations can be searched using their title, variants, or colors.

--- id: brandmark category: Brand title: Brandmark description: Logos/Brandmarks for UHG brands. design: https://www.figma.com/design/tk08Md4NBBVUPNHQYthmqp?node-id=0-21 --- ```jsx import { Brandmark } from '@uhg-abyss/mobile'; ``` ```tsx example () => { return ( ); }; ``` ## Brand Use the `brand` property to adjust which brand is being selected. ```tsx example () => { return ( ); }; ``` ## Size Use the `size` property to adjust the size of the brandmark. ```tsx example () => { return ( ); }; ``` ## Variant Use the `variant` property to select the required brandmark variants. ```tsx example () => { return ( ); }; ``` ## Color Use the `color` property to select available brandmark colors. ```tsx example () => { return ( ); }; ``` ### Brandmark Props ## Brandmark Props | Prop | Type | Description | Default | Required | |------|------|-------------|---------|----------| | `affiliate` | `'aarp_extra_assurance_benefits' \| 'aarp_medicare_plans' \| 'aarp_medicare_prescription' \| 'aarp_medicare_advantage' \| 'aarp_medicare_supplement' \| 'aarp_supplemental_personal_health' \| 'community_plan' \| 'dental' \| 'dual_complete' \| 'global' \| 'hearing' \| 'medicare_advantage' \| 'medicare_plans' \| 'medicare_solutions' \| 'oxford' \| 'uhc' \| 'vision' \| 'optum' \| 'optum_financial' \| 'optum_frontier_therapies' \| 'optum_perks' \| 'optum_prescription' \| 'optum_serve' \| 'optum_store' \| 'uhg' \| 'surest'` | Indicates the brandmark affiliate | `-` | Yes | | `brand` | `'uhc' \| 'optum' \| 'uhg' \| 'surest' \| undefined` | Indicates which brand you want your brandmark from | `-` | No | | `color` | `string \| undefined` | Set color option of the brandmark | `-` | No | | `onError` | `() => void \| undefined` | Callback function when the brandmark has an error loading | `-` | No | | `onLoad` | `() => void \| undefined` | Callback function when the brandmark is loaded | `-` | No | | `size` | `'$sm' \| '$md' \| '$lg' \| number \| undefined` | Set size of brandmark | `200` | No | | `sizes` | `Record \| undefined` | Pre programmed sizes for the brandmark | `'{ sm: 100, md: 150, lg: 200 }'` | No | | `title` | `string \| undefined` | Set title for the brandmark | `-` | No | | `variant` | `'lockup' \| 'lockup_horizontal' \| 'u_mark' \| 'u_mark_horizontal' \| 'monogram' \| 'stacked_wordmark' \| 'wordmark' \| 'lockup' \| 'lockup' \| 's_mark'` | Indicates the brandmark variant | `-` | Yes | ### Brandmark Classes ```tsx example () => { return

Brandmarks

; }; ```
You can use the search functionality to find the required brandmark. Brandmarks can be searched using their affiliates, variants or colors.
### Brandmark Tokens
--- id: components title: Components --- Browse Abyss mobile components. :::tip Scroll down within the Viewport Width to see more components. ::: --- id: flat-list category: Core title: FlatList description: A performant interface for rendering basic flat lists. sourceIsTS: true --- ## Usage The `FlatList` component is a high-performance list component that efficiently renders only the items currently visible on the screen, regardless of the size of the data set. It is ideal for rendering basic, flat lists and supports the most handy features like: - Full tokenization support. - Fully cross-platform. - Optional horizontal mode. - Configurable viewability callbacks. - Header support. - Footer support. - Separator support. - Pull to Refresh. - Scroll loading. - ScrollToIndex support. - Multiple column support. :::tip If you need section support, consider using the [SectionList](/mobile/core/section-list) component. ::: ```tsx example () => { const data = [ { id: 'bd7acbea-c1b1-46c2-aed5-3ad53abb28ba', title: 'One', }, { id: '3ac68afc-c605-48d3-a4f8-fbd91aa97f63', title: 'Two', }, { id: '58694a0f-3da1-471f-bd96-145571e29d72', title: 'Three', }, { id: '9b7acbea-c1b1-46c2-aed5-3ad53abb28ba', title: 'Four', }, { id: '8ac68afc-c605-48d3-a4f8-fbd91aa97f63', title: 'Five', }, { id: '78694a0f-3da1-471f-bd96-145571e29d72', title: 'Six', }, { id: '6b7acbea-c1b1-46c2-aed5-3ad53abb28ba', title: 'Seven', }, { id: '5ac68afc-c605-48d3-a4f8-fbd91aa97f63', title: 'Eight', }, ]; const ItemWrapper = styled('View', { width: '50%', padding: '$semantic.spacing.lg', }); const ItemContainer = styled('View', { backgroundColor: '$semantic.color.surface.container.emphasis.3', padding: '$semantic.spacing.lg', borderWidth: 4, borderColor: '$semantic.color.border.status.saturated.info', alignItems: 'center', borderRadius: '$semantic.border-radius.container.main', }); const Category = styled('View', { height: 50, alignItems: 'center', justifyContent: 'center', }); const Item = ({ title }) => { return ( {title} ); }; return ( { return ; }} keyExtractor={(item) => { return item.id; }} numColumns={2} ListHeaderComponent={ FlatList Header } ListHeaderComponentStyle={{ backgroundColor: '$semantic.color.surface.container.primary', borderColor: '$semantic.color.border.status.saturated.info', borderWidth: 6, marginHorizontal: '$semantic.spacing.lg', borderRadius: '$semantic.border-radius.container.main', }} columnWrapperStyle={{ borderWidth: 6, borderColor: '$semantic.color.border.status.saturated.info', marginVertical: '$semantic.spacing.xs', backgroundColor: '$semantic.color.surface.container.primary', borderRadius: '$semantic.border-radius.container.main', }} contentContainerStyle={{ backgroundColor: '$semantic.color.surface.container.tertiary', padding: '$semantic.spacing.lg', }} ListFooterComponentStyle={{ backgroundColor: '$semantic.color.surface.container.primary', borderColor: '$semantic.color.border.status.saturated.info', borderWidth: 6, marginHorizontal: '$semantic.spacing.lg', borderRadius: '$semantic.border-radius.container.main', }} ListFooterComponent={ FlatList Footer } /> ); }; ``` ### Best Practices - **Use Memoization:** Use [React.memo()](https://react.dev/reference/react/memo) to avoid unnecessary re-renders of list items. - **Pagination:** For large datasets, implement pagination with `onEndReached` to load additional data dynamically. - **Key Extraction:** Ensure `keyExtractor` returns a unique and stable key to avoid performance degradation caused by reordering or re-rendering items unnecessarily. ## Considerations _`FlatList` is a convenience wrapper around [VirtualizedList](/mobile/core/virtualized-list), and thus inherits its props (as well as those of [ScrollView](/mobile/core/scroll-view)) that aren't explicitly listed here, along with the following caveats:_ - **Internal State:** Internal state is not preserved when content scrolls out of the render window. Ensure all your data is captured in the item data or external stores like Flux, Redux, or Relay. - **Prop Updates:** This is a `PureComponent` meaning it will not re-render if props remain shallow-equal. Make sure that everything your `renderItem` function depends on is passed as a prop (e.g. `extraData`) that is not `===` after updates, otherwise your UI may not update on changes. This includes the `data` prop and parent component state. - **Item Rendering:** In order to constrain memory and enable smooth scrolling, content is rendered asynchronously offscreen. This means it's possible to scroll faster than the fill rate and momentarily see blank content. This is a tradeoff that can be adjusted to suit the needs of each application. - **Key Management:** By default, the list looks for a `key` prop on each item and uses that for the React key. Alternatively, you can provide a custom `keyExtractor` prop. ### Accessibility Considerations - **Screen Reader Support:** Ensure that list items have accessible labels and descriptions, especially if they contain interactive elements. - **Focus Management:** When dynamically loading data, manage focus properly so users can navigate the list without losing track of their position. ### Performance Considerations - **Windowing:** Use `initialNumToRender` and `maxToRenderPerBatch` props to control how many items are rendered initially and in each batch to avoid overloading the UI with too many items at once. - **Recycling Cells:** Consider using `CellRenderComponent` to recycle rendered items and improve rendering performance for large lists. - **Avoid Excessive Renders:** Leverage `shouldComponentUpdate` to prevent unnecessary renders of list items. ### FlatList Classes ### FlatList Props ## FlatList Props | Prop | Type | Description | Default | Required | |------|------|-------------|---------|----------| | `columnWrapperStyle` | `Abyss.Style<'View'> \| undefined` | Optional custom style for multi-item rows generated when `numColumns > 1` | `-` | No | | `contentContainerStyle` | `Abyss.Style<'View'> \| undefined` | These styles will be applied to the scroll view content container which wraps all of the child views. | `-` | No | | `contentInset` | `Insets \| undefined` | The amount by which the scroll view content is inset from the edges of the scroll view. Defaults to `{top: 0, left: 0, bottom: 0, right: 0}`. | `-` | No | | `contentOffset` | `PointProp \| undefined` | Used to manually set the starting scroll offset. The default value is `{ x: 0, y: 0 }` | `-` | No | | `endFillColor` | `Abyss.Color \| undefined` | Sometimes a ScrollView takes up more space than its content fills. When this is the case, this prop will fill the rest of the ScrollView with a color to avoid setting a background and creating unnecessary overdraw. This is an advanced optimization that is not needed in the general case. | `-` | No | | `fadingEdgeLength` | `Abyss.Space \| undefined` | Fades out the edges of the scroll content. If the value is greater than 0, the fading edges will be set accordingly to the current scroll direction and position, indicating if there is more content to show. The default value is 0. | `-` | No | | `hitSlop` | `number \| Insets \| null \| undefined` | This defines how far a touch event can start away from the view. Typical interface guidelines recommend touch targets that are at least 30 - 40 points/density-independent pixels. If a Touchable view has a height of 20 the touchable height can be extended to 40 with `hitSlop={{ top: 10, bottom: 10, left: 0, right: 0 }}` NOTE The touch area never extends past the parent view bounds and the Z-index of sibling views always takes precedence if a touch hits two overlapping views. | `-` | No | | `ListFooterComponentStyle` | `Abyss.Style<'View'> \| undefined` | Styling for internal View for ListFooterComponent | `-` | No | | `ListHeaderComponentStyle` | `Abyss.Style<'View'> \| undefined` | Styling for internal View for ListHeaderComponent | `-` | No | | `style` | `Abyss.Style<'FlatList'> \| undefined` | FlatList style properties with Abyss token mapping | `-` | No | --- id: image category: Core title: Image description: Displays different types of images, including network images, static resources, temporary local images, and images from local disk, such as the camera roll. sourceIsTS: true --- ```jsx import { Image } from '@uhg-abyss/mobile'; ``` ## Usage The Image component is a fundamental UI element that displays images in your application. This component is a customized extension of React Native's `Image` component, which is enhanced to fit seamlessly within our design system. It supports various image formats, allows for custom styling, and includes optimizations for loading performance. :::warning Important For network and data images, you will need to manually specify the dimensions of your image! ::: ```tsx example () => { return ( ); }; ``` You can also use the `style` prop with custom design tokens to style the image. ```tsx example () => { return ( ); }; ``` ## GIF and WebP support on Android When building your own native code, GIF and WebP are not supported by default on Android. You will need to add some optional modules in `android/app/build.gradle`, depending on the needs of your app. ``` dependencies { // If your app supports Android versions before Ice Cream Sandwich (API level 14) implementation 'com.facebook.fresco:animated-base-support:1.3.0' // For animated GIF support implementation 'com.facebook.fresco:animated-gif:3.1.3' // For WebP support, including animated WebP implementation 'com.facebook.fresco:animated-webp:3.1.3' implementation 'com.facebook.fresco:webpsupport:3.1.3' // For WebP support, without animations implementation 'com.facebook.fresco:webpsupport:3.1.3' } ``` ## Best Practices - **Use Accessibility Label:** Provide meaningful accessibility labels for accessibility. This is especially important for images that convey important information. - **Optimize Images:** Use appropriately sized images to reduce load times and improve performance. Compress images and use formats that balance quality and file size. - **Minimize Re-Renders:** Avoid unnecessary re-renders of the Image component, especially for large or high-resolution images. ### Image Classes ### Image Props ## Image Props | Prop | Type | Description | Default | Required | |------|------|-------------|---------|----------| | `capInsets` | `Insets \| undefined` | When the image is resized, the corners of the size specified by `capInsets` will stay a fixed size, but the center content and borders of the image will be stretched. This is useful for creating resizable rounded buttons, shadows, and other resizable assets. More info in the [official Apple documentation](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIImage_Class/index.html#//apple_ref/occ/instm/UIImage/resizableImageWithCapInsets). | `-` | No | | `height` | `Abyss.Size \| undefined` | Height of the image component. | `-` | No | | `style` | `Abyss.Style<"Image">` | Image style properties with Abyss token mapping | `-` | No | | `tintColor` | `Abyss.Color \| undefined` | Changes the color of all non-transparent pixels to the `tintColor`. | `-` | No | | `width` | `Abyss.Size \| undefined` | Width of the image component. | `-` | No | --- id: image-background category: Core title: ImageBackground description: Display an image as the background of another component. sourceIsTS: true --- ```jsx import { ImageBackground } from '@uhg-abyss/mobile'; ``` ## Usage A common feature request from developers familiar with the web is a `background-image`. To handle this use case, you can use the `ImageBackground` component, which has the same props as `Image`, and add whatever children to it you would like to layer on top of it. The `ImageBackground` component is a specialized container that displays an image as the background of a view. This component is an enhancement of React Native's core `ImageBackground`, tailored to integrate seamlessly with our design system. ## Example ```tsx example () => { return ( Inside ); }; ``` ## Resize Mode The `resizeMode` prop controls how the image is resized within the bounds set by the `style` prop. It can be one of the following values: ```tsx example () => { const [mode, setMode] = useState('cover'); const changeMode = (newMode) => { return () => { setMode(newMode); }; }; const modes = ['cover', 'contain', 'stretch', 'repeat', 'center']; return ( Abyss {modes.map((m) => { return ( ); })} ); }; ``` ### ImageBackground Classes ### ImageBackground Props ## ImageBackground Props | Prop | Type | Description | Default | Required | |------|------|-------------|---------|----------| | `capInsets` | `Insets \| undefined` | When the image is resized, the corners of the size specified by `capInsets` will stay a fixed size, but the center content and borders of the image will be stretched. This is useful for creating resizable rounded buttons, shadows, and other resizable assets. More info in the [official Apple documentation](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIImage_Class/index.html#//apple_ref/occ/instm/UIImage/resizableImageWithCapInsets). | `-` | No | | `height` | `Abyss.Size \| undefined` | Height of the image component. | `-` | No | | `imageStyle` | `Abyss.Style<"Image">` | These styles will be applied to the Image component which is nested inside ImageBackground | `-` | No | | `style` | `Abyss.Style<"ImageBackground">` | ImageBackground style properties with Abyss token mapping | `-` | No | | `tintColor` | `Abyss.Color \| undefined` | Changes the color of all non-transparent pixels to the `tintColor`. | `-` | No | | `width` | `Abyss.Size \| undefined` | Width of the image component. | `-` | No | --- id: keyboard-avoiding-view category: Core title: KeyboardAvoidingView description: Automatically adjust its height, position, or bottom padding based on the keyboard height to remain visible while the virtual keyboard is displayed. sourceIsTS: true --- ```jsx import { KeyboardAvoidingView } from '@uhg-abyss/mobile'; ``` ## Usage The `KeyboardAvoidingView` component is designed to automatically adjust the layout of your application when the on-screen keyboard appears, ensuring that the content remains visible and accessible to the user. This component extends the `KeyboardAvoidingView` core component present in React Native, while also supporting tokens in the `style`, `contentContainerStyle`, and `keyboardVerticalOffset` props. ```jsx () => { const [value, setValue] = useState(''); return ( Press the Input ); }; const styles = StyleSheet.create({ heading: { marginBottom: '$semantic.spacing.sm', }, innerView: { padding: 24, flex: 1, justifyContent: 'space-between', }, }); ``` ### KeyboardAvoidingView Classes ### KeyboardAvoidingView Props ## KeyboardAvoidingView Props | Prop | Type | Description | Default | Required | |------|------|-------------|---------|----------| | `contentContainerStyle` | `Abyss.Style<"View">` | The style of the content container(View) when behavior is 'position'. | `-` | No | | `hitSlop` | `number \| Insets \| null \| undefined` | This defines how far a touch event can start away from the view. Typical interface guidelines recommend touch targets that are at least 30 - 40 points/density-independent pixels. If a Touchable view has a height of 20 the touchable height can be extended to 40 with `hitSlop={{ top: 10, bottom: 10, left: 0, right: 0 }}` NOTE The touch area never extends past the parent view bounds and the Z-index of sibling views always takes precedence if a touch hits two overlapping views. | `-` | No | | `keyboardVerticalOffset` | `Abyss.Space \| undefined` | This is the distance between the top of the user screen and the react native view, may be non-zero in some use cases. | `-` | No | | `style` | `Abyss.Style<"KeyboardAvoidingView">` | KeyboardAvoidingView style properties with Abyss token mapping | `-` | No | --- id: keyboard-aware-scroll-view category: Core title: KeyboardAwareScrollView description: A ScrollView component that automatically handles keyboard appearance and scrolls to focused TextInput. sourceIsTS: true --- ```jsx import { KeyboardAwareScrollView } from '@uhg-abyss/mobile'; ``` ## Usage The `KeyboardAwareScrollView` component is a wrapper around [react-native-keyboard-aware-scroll-view's](https://github.com/APSL/react-native-keyboard-aware-scroll-view) `KeyboardAwareScrollView` component that automatically adjusts its content when the keyboard appears. It's particularly useful for forms and other input-heavy screens where you want to ensure that the focused input remains visible when the keyboard is shown. ```jsx () => { const [value1, setValue1] = useState(''); const [value2, setValue2] = useState(''); return ( Form Example ); }; ``` ## Platform Differences - On iOS, the component uses `react-native-keyboard-aware-scroll-view` which provides smooth keyboard handling. - On Android, it falls back to the native `ScrollView` with basic keyboard handling. ### KeyboardAwareScrollView Classes ### KeyboardAwareScrollView Props ## KeyboardAwareScrollView Props | Prop | Type | Description | Default | Required | |------|------|-------------|---------|----------| | `children` | `React.ReactNode` | - | `-` | Yes | | `dismissKeyboardOnScroll` | `boolean \| undefined` | - | `-` | No | | `keyboardAware` | `boolean \| undefined` | - | `-` | No | --- id: pressable category: Core title: Pressable description: A Pressable component wrapper that can be used to provide touch feedback. sourceIsTS: true --- ## Usage Pressable is a Core Component that can detect various stages of press interactions on any of its defined children. ```jsx I'm pressable! ``` ### How It Works **On an element wrapped by Pressable:** - [onPressIn](https://reactnative.dev/docs/pressable#onpressin) is called when a press is activated. - [onPressOut](https://reactnative.dev/docs/pressable#onpressout) is called when the press gesture is deactivated. **After onPressIn, the user will either:** - Remove their finger, triggering [onPressOut](https://reactnative.dev/docs/pressable#onpressout) followed by [onPress](https://reactnative.dev/docs/pressable#onpress). - Hold their finger longer than 500 milliseconds before removing it, [onLongPress](https://reactnative.dev/docs/pressable#onlongpress) is triggered. _([onPressOut](https://reactnative.dev/docs/pressable#onpressout) will still fire when they remove their finger)_.
Diagram of the onPress events in sequence. Diagram from React Native Documentation
:::note The touch area never extends past the parent view bounds and the Z-index of sibling views always takes precedence if a touch hits two overlapping views. ::: ### HitRect & HitSlop Fingers are not the most precise instruments, and it is common for users to accidentally activate the wrong element or miss the activation area. To help, `Pressable` has an optional `HitRect` you can use to define how far a touch can register away from the wrapped element. Presses can start anywhere within a `HitRect`. `PressRect` allows presses to move beyond the element and its `HitRect` while maintaining activation and being eligible for a "press"—think of sliding your finger slowly away from a button you're pressing down on.
Diagram of the onPress events in sequence. Diagram from React Native Documentation
_You can set `HitRect` with `hitSlop` and set `PressRect` with `pressRetentionOffset`_. :::info More documentation `Pressable` uses React Native's `Pressability` API. For more information around the state machine flow of `Pressability` and how it works, check out the [`Pressability` implementation](https://github.com/facebook/react-native/blob/main/packages/react-native/Libraries/Pressability/Pressability.js#L350). ::: ### Styling The `style` prop can be a function, an array of objects, or an object that defines the style of the `Pressable` component with design tokens. When using a function, the function will receive the `pressed` state as an argument. Additionally, the `children` prop can be a function that receives the `pressed` state as an argument or a React element. ```tsx example () => { return ( { return { width: 125, height: 125, borderWidth: pressed ? 10 : 4, borderRadius: '$core.border-radius.xl', borderColor: pressed ? '$semantic.color.border.status.subtle.success' : '$semantic.color.border.status.subtle.error', backgroundColor: pressed ? '$semantic.color.surface.container.status.success.tint' : '$semantic.color.surface.container.status.error.tint', margin: '$semantic.spacing.lg', padding: '$semantic.spacing.sm', alignItems: 'center', justifyContent: 'center', }; }} onPress={() => { console.log('onPress Called!'); }} onPressIn={() => { console.log('onPressIn Called!'); }} onPressOut={() => { console.log('onPressOut Called!'); }} onLongPress={() => { console.log('onLongPress Called!'); }} > {({ pressed }) => { return ( {pressed ? 'Release Me' : 'Press Me'} ); }} ); }; ``` ### Best Practices - **Press Feedback:** Always provide clear visual feedback (like color changes or animations) to indicate the element has been pressed. - **Hit Slop:** Use the `hitSlop` prop to extend the touchable area if needed, especially for smaller elements. - **Optimize Press Timing**: Use `onPressIn` and `onPressOut` wisely to handle animations or delays without affecting user experience. ## Accessibility Considerations - **Pressable Texts:** Ensure that the label of the pressable is descriptive for screen readers. - **Feedback for Press**: Make sure the visual feedback is discernible for all users, including those with visual impairments. - **Keyboard Navigation:** Ensure that pressable components are focusable and navigable with a keyboard.
### Pressable Classes ### Pressable Props ## Pressable Props | Prop | Type | Description | Default | Required | |------|------|-------------|---------|----------| | `android_ripple` | `PressableAndroidRippleConfig \| null \| undefined` | Enables the Android ripple effect and configures its color. | `-` | No | | `hitSlop` | `Insets \| Inset \| null \| undefined` | This defines how far a touch event can start away from the view. Typical interface guidelines recommend touch targets that are at least 30 - 40 points/density-independent pixels. If a Touchable view has a height of 20 the touchable height can be extended to 40 with `hitSlop={{ top: 10, bottom: 10, left: 0, right: 0 }}` NOTE The touch area never extends past the parent view bounds and the Z-index of sibling views always takes precedence if a touch hits two overlapping views. | `-` | No | | `pressRetentionOffset` | `Insets \| Inset \| null \| undefined` | Additional distance outside of this view in which a touch is considered a press before `onPressOut` is triggered. | `-` | No | | `style` | `((state: PressableStateCallbackType) => Abyss.Style<"Pressable">) \| Abyss.Style<"Pressable">` | Pressable style properties with Abyss token mapping | `-` | No |
--- id: refresh-control category: Core title: RefreshControl description: A standard control that can initiate the refreshing of a scroll view's contents. sourceIsTS: true --- ```jsx import { RefreshControl } from '@uhg-abyss/mobile'; ``` ## Usage The `RefreshControl` component is used to implement pull-to-refresh functionality in scrollable views, such as `ScrollView`, `FlatList`, or `SectionList`. This component is a customized version of React Native's core RefreshControl, offering enhanced styling, animations, and better integration with our design system. :::note `refreshing` is a controlled prop, which is why it needs to be set to `true` in the `onRefresh` function. Otherwise, the refresh indicator will stop immediately. ::: ```jsx () => { const [refreshing, setRefreshing] = useState(false); const onRefresh = useCallback(() => { setRefreshing(true); setTimeout(() => { setRefreshing(false); }, 4500); }, []); return ( } > Pull down to see RefreshControl indicator ); }; ``` ### RefreshControl Classes ### RefreshControl Props ## RefreshControl Props | Prop | Type | Description | Default | Required | |------|------|-------------|---------|----------| | `colors` | `Abyss.Color[] \| undefined` | The colors (at least one) that will be used to draw the refresh indicator | `-` | No | | `hitSlop` | `number \| Insets \| null \| undefined` | This defines how far a touch event can start away from the view. Typical interface guidelines recommend touch targets that are at least 30 - 40 points/density-independent pixels. If a Touchable view has a height of 20 the touchable height can be extended to 40 with `hitSlop={{ top: 10, bottom: 10, left: 0, right: 0 }}` NOTE The touch area never extends past the parent view bounds and the Z-index of sibling views always takes precedence if a touch hits two overlapping views. | `-` | No | | `progressBackgroundColor` | `Abyss.Color \| undefined` | The background color of the refresh indicator | `-` | No | | `progressViewOffset` | `Abyss.Space \| undefined` | The distance between the refresh indicator and the top of the view | `-` | No | | `style` | `Abyss.Style<"RefreshControl">` | RefreshControl style properties with Abyss token mapping | `-` | No | | `tintColor` | `Abyss.Color \| undefined` | The color of the refresh indicator | `-` | No | | `titleColor` | `Abyss.Color \| undefined` | The color of the refresh indicator title | `-` | No | --- id: safe-area-view category: Core title: SafeAreaView description: Render content within the safe area boundaries of a device. sourceIsTS: true --- ```jsx import { SafeAreaView } from '@uhg-abyss/mobile'; ``` ## Usage The purpose of `SafeAreaView` is to render content within the safe area boundaries of a device. The Abyss SafeAreaView is built on top of the SafeAreaView component provided by the [react-native-safe-area-context](https://github.com/AppAndFlow/react-native-safe-area-context) library. Please install the library in your project to use the Abyss SafeAreaView component. If the library is not installed, the Abyss SafeAreaView will fallback to the React Native SafeAreaView component. `SafeAreaView` renders nested content and automatically applies padding to reflect the portion of the view that is not covered by navigation bars, tab bars, toolbars, and other ancestor views. Moreover, and most importantly, Safe Area's paddings reflect the physical limitation of the screen, such as rounded corners or camera notches (i.e. the sensor housing area on iPhone 13). ## Edges You can set the edges to apply the safe area insets to by using the `edges` prop. For example if you don't want insets to apply to the top edge because the view does not touch the top of the screen you can use: ```jsx ... ``` Optionally it can be set to an object `{ top?: EdgeMode, right?: EdgeMode, bottom?: EdgeMode, left?: EdgeMode }` where `EdgeMode = 'off' | 'additive' | 'maximum'`. Additive is a default mode and is the same as passing and edge in the array: `finalPadding = safeArea + padding`. Maximum mode will use safe area inset or padding/margin (depends on mode) if safe area is less: `finalPadding = max(safeArea, padding)`. For example if you want a floating UI element that should be at the bottom safe area edge on devices with safe area or 24px from the bottom of the screen on devices without safe area or if safe area is less than 24px: ```jsx ... ``` ## Example ```jsx This is a SafeAreaView that avoids the Status Bar This is a SafeAreaView that avoids the Home Bar ``` ```tsx example () => { const PhoneContainer = styled('View', { justifyContent: 'space-between', width: '100%', }); const PaddedView = styled('View', { paddingHorizontal: '$semantic.spacing.lg', backgroundColor: '$core.color.brand.100', width: '100%', variants: { placement: { top: { paddingTop: 40, paddingBottom: '$semantic.spacing.lg', }, bottom: { paddingBottom: 28, paddingTop: '$semantic.spacing.lg', }, }, }, }); const Label = styled('Text', { color: '$semantic.color.text.body.alt', fontSize: '$semantic.font-size.body.60', textAlign: 'center', }); return ( ); }; ``` ### SafeAreaView Classes ### SafeAreaView Props ## SafeAreaView Props | Prop | Type | Description | Default | Required | |------|------|-------------|---------|----------| | `edges` | `Edges \| undefined` | Sets the edges to apply the safe area insets to. For example if you don't want insets to apply to the top edge because the view does not touch the top of the screen you can use: ```tsx ``` You can also specify edge modes: ```tsx ``` - `'off'` - don't apply insets for the specified edge - `'additive'` - (default) add the safe area inset to the existing padding or margin - `'maximum'` - use the larger of the existing padding/margin or the safe area inset | `-` | No | | `hitSlop` | `number \| Insets \| null \| undefined` | This defines how far a touch event can start away from the view. Typical interface guidelines recommend touch targets that are at least 30 - 40 points/density-independent pixels. If a Touchable view has a height of 20 the touchable height can be extended to 40 with `hitSlop={{ top: 10, bottom: 10, left: 0, right: 0 }}` NOTE The touch area never extends past the parent view bounds and the Z-index of sibling views always takes precedence if a touch hits two overlapping views. | `-` | No | | `mode` | `"padding" \| "margin" \| undefined` | The mode to use to apply safe area insets. 'padding' will add the insets to the existing padding of the view, while 'margin' will add the insets to the existing margin of the view. Defaults to 'padding'. | `'padding'` | No | | `onBlur` | `Abyss.NativeSyntheticEventHandler` | Called when the view loses focus | `-` | No | | `onFocus` | `Abyss.NativeSyntheticEventHandler` | Called when the view gains focus | `-` | No | | `style` | `Abyss.Style<"SafeAreaView">` | SafeAreaView style properties with Abyss token mapping | `-` | No | --- id: scroll-view category: Core title: ScrollView description: A generic scrolling container that can host multiple components and views. sourceIsTS: true --- ## Usage The `ScrollView` component is a scrollable container that can hold a variety of elements, enabling vertical or horizontal scrolling. This customized version of React Native's [ScrollView](https://reactnative.dev/docs/scrollview) component integrates Abyss design tokens and performance optimizations for a smooth and responsive experience. ```tsx example () => { return ( Scroll Me Lorem ipsum odor amet, consectetuer adipiscing elit. Elit lacinia torquent et mauris habitasse netus efficitur aenean aptent. Finibus posuere maximus tortor, nisi bibendum ultricies. Tellus integer eu commodo sed pharetra mauris quam potenti mauris. Convallis nisl auctor risus mattis id. Himenaeos turpis egestas consequat tortor aliquam, dictumst integer volutpat eu. Quis leo ex parturient arcu sagittis. Et nostra platea vestibulum bibendum pharetra accumsan semper cursus. {'\n'} {'\n'} Dictum porttitor penatibus auctor nisl; sem porttitor curae quisque. Ipsum accumsan eu vestibulum ligula, vehicula integer. Nunc varius massa placerat conubia mus magna. Aliquet eleifend porttitor porta mattis consectetur habitasse at fringilla. Odio aptent aliquam sociosqu justo egestas adipiscing ac conubia. Bibendum ultrices commodo ante mus mollis netus. {'\n'} {'\n'} Bibendum taciti habitant ridiculus scelerisque aliquam varius lacus maximus. Efficitur facilisis parturient auctor accumsan nascetur ad phasellus lectus. Lacinia natoque conubia convallis habitant mauris eleifend. Turpis consectetur tempor egestas taciti; venenatis cursus? Cursus congue adipiscing purus neque vitae nibh? Risus accumsan ullamcorper velit eros tellus curae. Interdum nascetur morbi; pharetra id venenatis volutpat potenti. Convallis senectus praesent lectus nisi eget justo vitae. Venenatis ornare sociosqu euismod feugiat integer. Curae odio massa purus eu facilisis laoreet. {'\n'} {'\n'} Lectus curabitur scelerisque mus auctor nascetur iaculis ante risus. Tristique conubia nisl inceptos bibendum pellentesque. Eleifend imperdiet gravida pellentesque hendrerit eget dignissim magnis varius augue. Curabitur proin porttitor molestie gravida amet praesent et fringilla. Ante etiam at gravida efficitur cubilia rutrum torquent adipiscing. Parturient bibendum id convallis torquent venenatis. Lacinia primis leo nullam tincidunt consectetur quisque. Elementum tristique quis magnis ornare molestie venenatis. Sollicitudin netus class rutrum proin; curabitur facilisis convallis pretium sollicitudin. Feugiat volutpat eget arcu convallis ultricies id. ); }; ``` ### ScrollView vs FlatList `ScrollView` renders all its child components all at once, even if the component is not in view. This can negatively affect the performance of the app by requiring more processing power and memory usage when rendering a large number of items. This is where [FlatList](/mobile/core/flat-list) comes into play. `FlatList` renders items lazily, before they appear on screen. It also removes items that scroll off-screen to save memory and processing time. `FlatList` is also handy if you want to render separators between your items, multiple columns, infinite scrolling, or any number of other features it supports out of the box. ### Best Practices - **Minimize Overdraw:** Use a background color to avoid unnecessary redrawing of background elements. - **Limit Scrollable Content:** Avoid putting too many elements inside a `ScrollView`. If the content grows large, consider using a `FlatList` or `SectionList` for better performance. - **Optimize Images:** When scrolling with images, ensure they are properly sized and optimized to prevent memory issues. ## Considerations - **Bounded Height:** The `ScrollView` must have a bounded height, since they contain children with unbound-heights. - To set the height of a `ScrollView`, either define a height directly (discouraged) or make sure the parent Components have bounded height. - **Nested Responders:** `ScrollView` does not yet support preventing touch gestures on its children from becoming scroll gestures. - This doesn't mean that responders won't work inside of a `ScrollView`. It simply means `ScrollView` will prioritize its own responder if it detects a gesture that could be interpreted as a scroll. - _*For more information, check out React Native's [Gesture Responder System](https://reactnative.dev/docs/gesture-responder-system).*_ ### Accessibility Considerations - **Keyboard Focus:** Ensure that the content inside the `ScrollView` is reachable and visible when users navigate with a keyboard. - **Readable Labels:** Label any scrollable area for screen readers, so users know they can scroll through the content. - **Scroll Indicators:** Allow scroll indicators to be visible for accessibility users, so they are aware of the scrollable content. ### Performance Considerations - **Rendering Limits:** Avoid placing too many child components inside a `ScrollView` as it can cause performance bottlenecks. Instead, use lists (`FlatList`,`SectionList`) for large datasets. - **Lazy Loading:** Consider lazy loading for content-heavy views to avoid loading everything upfront. - **Batch Updates:** Ensure updates to the scroll view content are batched to reduce rendering overhead. ### ScrollView Classes ### ScrollView Props ## ScrollView Props | Prop | Type | Description | Default | Required | |------|------|-------------|---------|----------| | `contentContainerStyle` | `Abyss.Style<"View">` | These styles will be applied to the scroll view content container which wraps all of the child views. | `-` | No | | `contentInset` | `Insets \| undefined` | The amount by which the scroll view content is inset from the edges of the scroll view. Defaults to `{top: 0, left: 0, bottom: 0, right: 0}`. | `-` | No | | `contentOffset` | `PointProp \| undefined` | Used to manually set the starting scroll offset. The default value is `{ x: 0, y: 0 }` | `-` | No | | `endFillColor` | `Abyss.Color \| undefined` | Sometimes a ScrollView takes up more space than its content fills. When this is the case, this prop will fill the rest of the ScrollView with a color to avoid setting a background and creating unnecessary overdraw. This is an advanced optimization that is not needed in the general case. | `-` | No | | `fadingEdgeLength` | `Abyss.Space \| undefined` | Fades out the edges of the scroll content. If the value is greater than 0, the fading edges will be set accordingly to the current scroll direction and position, indicating if there is more content to show. The default value is 0. | `-` | No | | `hitSlop` | `number \| Insets \| null \| undefined` | This defines how far a touch event can start away from the view. Typical interface guidelines recommend touch targets that are at least 30 - 40 points/density-independent pixels. If a Touchable view has a height of 20 the touchable height can be extended to 40 with `hitSlop={{ top: 10, bottom: 10, left: 0, right: 0 }}` NOTE The touch area never extends past the parent view bounds and the Z-index of sibling views always takes precedence if a touch hits two overlapping views. | `-` | No | | `style` | `Abyss.Style<"ScrollView">` | ScrollView style properties with Abyss token mapping | `-` | No | --- id: section-list category: Core title: SectionList description: A performant interface for rendering sectioned lists. sourceIsTS: true --- ## Usage The `SectionList` component is a high-performance list component that efficiently renders sectioned lists. It is ideal for rendering lists with sections and supports the most handy features like: - Full tokenization support. - Fully cross-platform. - Configurable viewability callbacks. - List header support. - List footer support. - Item separator support. - Section header support. - Section separator support. - Heterogeneous data and item rendering support. - Pull to Refresh. - Scroll loading. :::tip If you don't need section support and want a simpler interface, use a [FlatList](/mobile/core/flat-list). ::: ```tsx example () => { const data = [ { title: 'Main Dishes', data: ['Pizza', 'Burger', 'Risotto'], }, { title: 'Sides', data: ['French Fries', 'Onion Rings', 'Fried Shrimps'], }, { title: 'Drinks', data: ['Water', 'Coke', 'Beer'], }, { title: 'Desserts', data: ['Cheese Cake', 'Ice Cream', 'Brownies'], }, ]; const Item = styled('Text', { backgroundColor: '$semantic.color.surface.container.emphasis.4', padding: '$semantic.spacing.lg', marginVertical: '$semantic.spacing.xs', fontSize: '$core.font-size.h.40', color: '$core.color.brand.100', borderWidth: 2.5, borderColor: '$core.color.brand.100', }); return ( { return item + index; }} renderItem={({ item }) => { return {item}; }} renderSectionHeader={({ section: { title } }) => { return ( {title} ); }} contentContainerStyle={{ padding: '$semantic.spacing.lg' }} /> ); }; ``` ### Best Practices - **Use Memoization:** Use [React.memo()](https://react.dev/reference/react/memo) to avoid unnecessary re-renders of list items. - **Section Headers:** Keep section headers concise to avoid taking up too much space. - **Key Management:** Ensure that the `keyExtractor` for individual items and sections is unique to avoid rerender issues. ## Considerations _`SectionList` is a convenience wrapper around [VirtualizedList](/mobile/core/virtualized-list), and thus inherits its props (as well as those of [ScrollView](/mobile/core/scroll-view)) that aren't explicitly listed here, along with the following caveats:_ - **Internal State:** Internal state is not preserved when content scrolls out of the render window. Ensure all your data is captured in the item data or external stores like Flux, Redux, or Relay. - **Prop Updates:** This is a `PureComponent` meaning it will not re-render if props remain shallow-equal. Make sure that everything your `renderItem` function depends on is passed as a prop (e.g. `extraData`) that is not `===` after updates, otherwise your UI may not update on changes. This includes the `data` prop and parent component state. - **Item Rendering:** In order to constrain memory and enable smooth scrolling, content is rendered asynchronously offscreen. This means it's possible to scroll faster than the fill rate and momentarily see blank content. This is a tradeoff that can be adjusted to suit the needs of each application. - **Key Management:** By default, the list looks for a `key` prop on each item and uses that for the React key. Alternatively, you can provide a custom `keyExtractor` prop. ### Accessibility Considerations - **Navigable Sections:** Ensure section headers and items are properly labeled for screen readers. - **Focus Management:** When rendering additional sections, ensure the user focus remains consistent without jumping or losing context. ### Performance Considerations - **Batch Updates:** Use batch updates to avoid triggering multiple re-renders when updating section data. - **SectionHeader Optimization:** Memoize section headers to prevent unnecessary re-renders during list scrolls. - **Windowing:** Use `initialNumToRender` and `maxToRenderPerBatch` props to control how many items are rendered initially and in each batch to avoid overloading the UI with too many items at once. - **Recycling Cells:** Consider using `CellRenderComponent` to recycle rendered items and improve rendering performance for large lists. ### SectionList Classes ### SectionList Props ## SectionList Props | Prop | Type | Description | Default | Required | |------|------|-------------|---------|----------| | `contentContainerStyle` | `Abyss.Style<'View'> \| undefined` | These styles will be applied to the scroll view content container which wraps all of the child views. | `-` | No | | `contentInset` | `Insets \| undefined` | The amount by which the scroll view content is inset from the edges of the scroll view. Defaults to `{top: 0, left: 0, bottom: 0, right: 0}`. | `-` | No | | `contentOffset` | `PointProp \| undefined` | Used to manually set the starting scroll offset. The default value is `{ x: 0, y: 0 }` | `-` | No | | `endFillColor` | `Abyss.Color \| undefined` | Sometimes a ScrollView takes up more space than its content fills. When this is the case, this prop will fill the rest of the ScrollView with a color to avoid setting a background and creating unnecessary overdraw. This is an advanced optimization that is not needed in the general case. | `-` | No | | `fadingEdgeLength` | `Abyss.Space \| undefined` | Fades out the edges of the scroll content. If the value is greater than 0, the fading edges will be set accordingly to the current scroll direction and position, indicating if there is more content to show. The default value is 0. | `-` | No | | `hitSlop` | `number \| Insets \| null \| undefined` | This defines how far a touch event can start away from the view. Typical interface guidelines recommend touch targets that are at least 30 - 40 points/density-independent pixels. If a Touchable view has a height of 20 the touchable height can be extended to 40 with `hitSlop={{ top: 10, bottom: 10, left: 0, right: 0 }}` NOTE The touch area never extends past the parent view bounds and the Z-index of sibling views always takes precedence if a touch hits two overlapping views. | `-` | No | | `ListFooterComponentStyle` | `Abyss.Style<'View'> \| undefined` | Styling for internal View for ListFooterComponent | `-` | No | | `ListHeaderComponentStyle` | `Abyss.Style<'View'> \| undefined` | Styling for internal View for ListHeaderComponent | `-` | No | | `style` | `Abyss.Style<'SectionList'> \| undefined` | SectionList style properties with Abyss token mapping | `-` | No | --- id: status-bar category: Core title: StatusBar description: Component to control the app's status bar. sourceIsTS: true --- ```jsx import { StatusBar } from '@uhg-abyss/mobile'; ``` ## Usage The `StatusBar` component controls the app's status bar. The status bar is the zone, typically at the top of the screen, that displays the current time, Wi-Fi and cellular network information, battery level and/or other status icons. This component is a customized version of React Native's `StatusBar` core component, offering additional configuration options and integration with our design system. It allows you to manage the status bar's `style`, `visibility`, and `backgroundColor` to match the application's theme with our design tokens. ## Usage with Navigator It is possible to have multiple StatusBar components mounted at the same time. The props will be merged in the order the StatusBar components were mounted. ## Imperative API For cases where using a component is not ideal, there is also an imperative API exposed as static functions on the component. However, it is not recommended to use the static API and the component for the same prop because any value set by the static API will get overridden by the one set by the component in the next render. ## Performance Considerations The StatusBar is a lightweight component, but to ensure optimal performance: - **Minimize Dynamic Changes:** Limit frequent updates to the status bar's properties, as unnecessary changes can impact performance, especially on lower-end devices. - **Use Animations Judiciously:** While animations can enhance the user experience, use them sparingly to avoid performance degradation. ## Accessibility Considerations When configuring the StatusBar, ensure that the chosen colors and styles provide sufficient contrast and readability. The status bar should be easily readable in various lighting conditions and should not obscure critical content or UI elements. ### StatusBar Classes ### StatusBar Props ## StatusBar Props | Prop | Type | Description | Default | Required | |------|------|-------------|---------|----------| | `backgroundColor` | `Abyss.Color \| undefined` | Background color of the status bar. | `-` | No | --- id: touchable-highlight category: Core title: TouchableHighlight description: A wrapper for making views respond properly to touches. sourceIsTS: true --- :::tip If you're looking for a more extensive and future-proof way to handle touch-based input, check out the [`Pressable` API](/mobile/core/pressable). ::: ## Usage `TouchableHighlight` is a wrapper for handling pressEvents of a `View`. While pressing down, the opacity of the wrapped view is decreased, allowing the underlay color to show through. ```jsx function MyComponent(props: MyComponentProps) { return ( My Component ); } alert('Pressed!')} > ; ``` ```tsx example () => { return ( {}} > Gray Touchable Highlight ); }; ``` ## Considerations - **Visual Artifacts:** The underlay comes from wrapping the child in a `View` component. This can sometimes cause unwanted visual artifacts and affect layout if not used correctly. For example, if the `backgroundColor` of the wrapped `View` is not explicitly set to an opaque color. - **Children:** `TouchableHighlight` can only have a single child. If you wish to have several children, wrap them in a `View`. ### Accessibility Considerations - **Feedback for Users:** Ensure that the visual feedback (`underlayColor` color) is noticeable for users with visual impairments. - **Screen Reader Support:** Label the component appropriately for screen readers so users understand the interaction. - **Keyboard Focus:** Ensure the component can be focused and activated using a keyboard for accessibility. ### TouchableHighlight Classes ### TouchableHighlight Props ## TouchableHighlight Props | Prop | Type | Description | Default | Required | |------|------|-------------|---------|----------| | `activeOpacity` | `Abyss.Opacity \| undefined` | Determines what the opacity of the wrapped view should be when touch is active | `-` | No | | `hitSlop` | `Insets \| Inset \| null \| undefined` | This defines how far your touch can start away from the button. This is added to pressRetentionOffset when moving off of the button. NOTE The touch area never extends past the parent view bounds and the Z-index of sibling views always takes precedence if a touch hits two overlapping views. | `-` | No | | `pressRetentionOffset` | `Insets \| Inset \| null \| undefined` | When the scroll view is disabled, this defines how far your touch may move off of the button, before deactivating the button. Once deactivated, try moving it back and you'll see that the button is once again reactivated! Move it back and forth several times while the scroll view is disabled. Ensure you pass in a constant to reduce memory allocations. | `-` | No | | `style` | `Abyss.Style<"TouchableHighlight">` | TouchableHighlight style properties with Abyss token mapping | `-` | No | | `underlayColor` | `Abyss.Color \| undefined` | The color of the underlay that will show through when the touch is active | `-` | No | --- id: touchable-opacity category: Core title: TouchableOpacity description: A wrapper for making views respond properly to touches. sourceIsTS: true --- :::tip If you're looking for a more extensive and future-proof way to handle touch-based input, check out the [`Pressable` API](/mobile/core/pressable). ::: ## Usage The `TouchableOpacity` component is used to create pressable elements that fade out on press, providing smooth and subtle visual feedback. This component is lightweight and often used for buttons or other pressable elements that require an opacity change on interaction. This customized version of the React Native `TouchableOpacity` component is enhanced to supports Abyss design tokens and fit seamlessly into our design system. Opacity is controlled by wrapping the children in an `Animated.View`, which is added to the view hierarchy. Be aware that this can affect layout. ```tsx example () => { const styles = StyleSheet.create({ button: { borderWidth: 2, padding: '$semantic.spacing.lg', alignItems: 'center', borderRadius: 100, borderColor: '$semantic.color.border.status.saturated.info', backgroundColor: '$semantic.color.surface.container.emphasis.4', }, }); return ( Opacity Button ); }; ``` ### Best Practices - **Consistent Opacity:** Use consistent values for activeOpacity across your app to maintain uniformity in user interactions. - **Clear Press Feedback:** Ensure that the change in opacity is noticeable enough to signal to users that the element is pressed. - **Layering Components:** Be mindful when layering TouchableOpacity over complex backgrounds, as the fade effect might not be as visible. ## Accessibility Considerations - **Visual Feedback:** Ensure the fade effect is sufficiently noticeable for all users, especially those with visual impairments. - **Keyboard Navigation:** Ensure the component can be navigated and activated via a keyboard. - **Accessible Labels:** Add descriptive labels to the TouchableOpacity component so screen readers can convey its functionality. ### TouchableOpacity Classes ### TouchableOpacity Props ## TouchableOpacity Props | Prop | Type | Description | Default | Required | |------|------|-------------|---------|----------| | `activeOpacity` | `Abyss.Opacity \| undefined` | Determines what the opacity of the wrapped view should be when touch is active | `-` | No | | `hitSlop` | `Insets \| Inset \| null \| undefined` | This defines how far your touch can start away from the button. This is added to pressRetentionOffset when moving off of the button. NOTE The touch area never extends past the parent view bounds and the Z-index of sibling views always takes precedence if a touch hits two overlapping views. | `-` | No | | `pressRetentionOffset` | `Insets \| Inset \| null \| undefined` | When the scroll view is disabled, this defines how far your touch may move off of the button, before deactivating the button. Once deactivated, try moving it back and you'll see that the button is once again reactivated! Move it back and forth several times while the scroll view is disabled. Ensure you pass in a constant to reduce memory allocations. | `-` | No | | `style` | `Abyss.Style<"TouchableOpacity">` | TouchableOpacity style properties with Abyss token mapping | `-` | No | --- id: touchable-without-feedback category: Core title: TouchableWithoutFeedback description: Captures touch events without providing any visual feedback. sourceIsTS: true --- :::tip If you're looking for a more extensive and future-proof way to handle touch-based input, check out the [`Pressable` API](/mobile/core/pressable). ::: ## Usage The `TouchableWithoutFeedback` component is a wrapper that captures touch events without providing any visual feedback. It is ideal for handling touch events on components that don't need to display any visual feedback. :::warning Do not use unless you have a very good reason. All elements that respond to press should have a visual feedback when touched. ::: ```tsx function MyComponent(props: MyComponentProps) { return ( My Component ); } alert('Pressed!')}> ; ``` ### Example ```tsx example () => { const Container = styled('View', { flex: 1, justifyContent: 'center', paddingHorizontal: '$semantic.spacing.lg', }); const CountContainer = styled('View', { alignItems: 'center', padding: '$semantic.spacing.lg', }); const Count = styled('Text', { color: '$semantic.color.text.label.status.info', }); const ButtonView = styled('View', { alignItems: 'center', backgroundColor: '$semantic.color.surface.interactive.standards.active.secondary', padding: '$semantic.spacing.sm', }); const [count, setCount] = useState(0); const handlePress = () => { setCount(count + 1); }; return ( Count: {count} Increment ); }; ``` ### Best Practices - **Use Sparingly:** Only use `TouchableWithoutFeedback` when no visual feedback is required or desired. If feedback is expected, use `Pressable` or `TouchableOpacity` instead. - **Visual Feedback:** Ensure the child components provide sufficient visual feedback, even if `TouchableWithoutFeedback` does not. - **Invisible Buttons:** Consider accessibility implications if `TouchableWithoutFeedback` is used to create invisible or hidden touch areas. ## Considerations - **Number of Children:** `TouchableWithoutFeedback` only supports one child. If you wish to have several child components, wrap them in a View. - **Prop Spread:** `TouchableWithoutFeedback` Does not handle touch events directly, it clones its child and applies responder props to the clone. Therefore it is important that any intermediary components pass props through to the underlying React Native component. ### Accessibility Considerations - **Keyboard Navigation:** Ensure the component is accessible via keyboard and focusable if needed. - **Screen Reader Labels:** If touchable areas are hidden or provide no feedback, ensure the interactive area is described accurately for screen readers. ### TouchableWithoutFeedback Classes ### TouchableWithoutFeedback Props ## TouchableWithoutFeedback Props | Prop | Type | Description | Default | Required | |------|------|-------------|---------|----------| | `hitSlop` | `Insets \| Inset \| null \| undefined` | This defines how far your touch can start away from the button. This is added to pressRetentionOffset when moving off of the button. NOTE The touch area never extends past the parent view bounds and the Z-index of sibling views always takes precedence if a touch hits two overlapping views. | `-` | No | | `pressRetentionOffset` | `Insets \| Inset \| null \| undefined` | When the scroll view is disabled, this defines how far your touch may move off of the button, before deactivating the button. Once deactivated, try moving it back and you'll see that the button is once again reactivated! Move it back and forth several times while the scroll view is disabled. Ensure you pass in a constant to reduce memory allocations. | `-` | No | | `style` | `Abyss.Style<"TouchableWithoutFeedback">` | TouchableWithoutFeedback style properties with Abyss token mapping | `-` | No | --- id: view category: Core title: View description: The most fundamental core component for building a UI, a container that supports layout. sourceIsTS: true --- ```jsx import { View } from '@uhg-abyss/mobile'; ``` ## Usage The `View` component is one of the fundamental building blocks of a user interface. It functions as a container that supports layout, styling, and interaction handling. The component serves as a versatile wrapper for organizing and displaying other components within a user interface. While it extends the core View component present in React Native, the `View` component in Abyss Mobile allows using tokens directly in the style prop. This feature enables developers to use the design tokens defined in the theme to style a component. This example creates a View that wraps two boxes with color and a text component in a row with padding. Notice the use of design tokens in the style prop. ```tsx example () => { return ( Hello World! ); }; ``` :::info Styling Views are designed to be used with [`StyleSheet`](/mobile/ui/style-sheet) for clarity and performance, although inline styles are also supported. ::: ## Best Practices - **Use for Layout**: Utilize the `View` component to create layouts and organize components within a user interface. For text context, consider using the [Text component](/mobile/ui/text). - **Avoid Excessive Nesting**: Limit the number of nested `View` components to maintain a clean and efficient layout. - **Use Tokens**: Leverage design tokens in the style prop to ensure consistency and maintainability in styling. - **Accessibility**: Ensure that the content within the `View` component is accessible to all users by providing appropriate labels and descriptions where necessary. ## Performance Considerations To maximize performance, be mindful of: - **Shallow component trees**: Minimize nesting `View` components to reduce the complexity of the component tree. - **Avoid unnecessary re-renders**: Use `React.memo` or similar optimization when rendering complex or frequently changing layouts. ### View Classes ### View Props ## View Props | Prop | Type | Description | Default | Required | |------|------|-------------|---------|----------| | `hitSlop` | `number \| Insets \| null \| undefined` | This defines how far a touch event can start away from the view. Typical interface guidelines recommend touch targets that are at least 30 - 40 points/density-independent pixels. If a Touchable view has a height of 20 the touchable height can be extended to 40 with `hitSlop={{ top: 10, bottom: 10, left: 0, right: 0 }}` NOTE The touch area never extends past the parent view bounds and the Z-index of sibling views always takes precedence if a touch hits two overlapping views. | `-` | No | | `onBlur` | `Abyss.NativeSyntheticEventHandler` | Called when the view loses focus | `-` | No | | `onFocus` | `Abyss.NativeSyntheticEventHandler` | Called when the view gains focus | `-` | No | | `style` | `Abyss.Style<"View">` | View style properties with Abyss token mapping | `-` | No | --- id: virtualized-list category: Core title: VirtualizedList description: A high-performance list component that efficiently renders only the items currently visible on the screen, regardless of the size of the data set. sourceIsTS: true --- ## Usage The `VirtualizedList` component is a high-performance list component that efficiently renders only the items currently visible on the screen, regardless of the size of the data set. This is the base implementation of the [FlatList](/mobile/core/flat-list) and [SectionList](/mobile/core/section-list) components. In general, this should only be used if you need more flexibility than FlatList or SectionList can provides, e.g. for use with immutable data instead of plain arrays. Virtualization massively improves the performance and memory consumption of large lists by maintaining a finite render window showing only active items and replacing all items outside of the render window with appropriately sized blank space. The render window adapts with scrolling behavior, items are rendered incrementally with _low-pri_ (after any running interactions) if they are far from the visible area, or with _hi-pri_ if they are near the visible area. ```tsx example () => { const ItemContainer = styled('View', { backgroundColor: '$semantic.color.surface.accent.secondary', height: 150, justifyContent: 'center', marginVertical: '$semantic.spacing.sm', marginHorizontal: '$semantic.spacing.lg', padding: 20, }); const ItemText = styled('Text', { fontSize: 32, }); const Item = ({ title }) => { return ( {title} ); }; const getItem = (_data, index) => { return { id: Math.random().toString(12).substring(0), title: `Item ${index + 1}`, }; }; return ( { return ; }} keyExtractor={(item) => { return item.id; }} getItemCount={() => { return 50; }} getItem={getItem} ListFooterComponent={} ListHeaderComponent={} ListHeaderComponentStyle={{ borderColor: '$semantic.color.border.interactive.buttons.active.alt', borderWidth: 4, marginHorizontal: '$semantic.spacing.lg', }} ListFooterComponentStyle={{ borderColor: '$semantic.color.border.interactive.buttons.active.alt', borderWidth: 4, marginHorizontal: '$semantic.spacing.lg', }} /> ); }; ``` ### Best Practices - **Pagination:** Implement infinite scrolling by utilizing the `onEndReached` prop for large datasets. - **Use Memoization:** Use [React.memo()](https://react.dev/reference/react/memo) to avoid unnecessary re-renders of list items. - **Lazy Load Images:** If the list contains images, ensure they are lazy-loaded to avoid consuming memory unnecessarily. - **Key Management:** Ensure `keyExtractor` returns a unique and stable key to avoid performance degradation caused by reordering or re-rendering items unnecessarily. ## Considerations - **Internal State:** Internal state is not preserved when content scrolls out of the render window. Ensure all your data is captured in the item data or external stores like _Flux_, _Redux_, or _Relay_. - **Prop Updates:** This is a `PureComponent` meaning it will not re-render if props remain shallow-equal. Make sure that everything your `renderItem` function depends on is in passed as a prop (e.g. `extraData`) that is not `===` after updates, otherwise your UI may not update on changes. This includes the `data` prop and parent component state. - **Item Rendering:** To constrain memory and enable smooth scrolling, content is rendered asynchronously offscreen. This means it's possible to scroll faster than the fill rate and momentarily see blank content. This is a tradeoff that can be adjusted to suit the needs of each application. - **Key Management:** By default, the list looks for a `key` prop on each item and uses that for the React key. Alternatively, you can provide a custom `keyExtractor` prop. ### Accessibility Considerations - **Focus Retention:** Retain focus and scroll position when adding or removing items from the list. - **Content Labeling:** Ensure each list item has accessible labels or descriptions for screen readers. ### Performance Considerations - **Windowing:** Adjust the `initialNumToRender` and `windowSize` props to strike a balance between performance and UX. - **Cell Recycling:** Use `CellRendererComponent` to efficiently recycle rendered items, especially for very large datasets. - **Avoid Expensive Re-renders:** Avoid triggering unnecessary re-renders by memoizing components or using `React.PureComponent`. ### VirtualizedList Classes ### VirtualizedList Props ## VirtualizedList Props | Prop | Type | Description | Default | Required | |------|------|-------------|---------|----------| | `contentContainerStyle` | `Abyss.Style<'View'> \| undefined` | These styles will be applied to the scroll view content container which wraps all of the child views. | `-` | No | | `contentInset` | `Insets \| undefined` | The amount by which the scroll view content is inset from the edges of the scroll view. Defaults to `{top: 0, left: 0, bottom: 0, right: 0}`. | `-` | No | | `contentOffset` | `PointProp \| undefined` | Used to manually set the starting scroll offset. The default value is `{ x: 0, y: 0 }` | `-` | No | | `endFillColor` | `Abyss.Color \| undefined` | Sometimes a ScrollView takes up more space than its content fills. When this is the case, this prop will fill the rest of the ScrollView with a color to avoid setting a background and creating unnecessary overdraw. This is an advanced optimization that is not needed in the general case. | `-` | No | | `fadingEdgeLength` | `Abyss.Space \| undefined` | Fades out the edges of the scroll content. If the value is greater than 0, the fading edges will be set accordingly to the current scroll direction and position, indicating if there is more content to show. The default value is 0. | `-` | No | | `hitSlop` | `number \| Insets \| null \| undefined` | This defines how far a touch event can start away from the view. Typical interface guidelines recommend touch targets that are at least 30 - 40 points/density-independent pixels. If a Touchable view has a height of 20 the touchable height can be extended to 40 with `hitSlop={{ top: 10, bottom: 10, left: 0, right: 0 }}` NOTE The touch area never extends past the parent view bounds and the Z-index of sibling views always takes precedence if a touch hits two overlapping views. | `-` | No | | `ListFooterComponentStyle` | `Abyss.Style<'View'> \| undefined` | Styling for internal View for ListFooterComponent | `-` | No | | `ListHeaderComponentStyle` | `Abyss.Style<'View'> \| undefined` | Styling for internal View for ListHeaderComponent | `-` | No | | `style` | `Abyss.Style<'VirtualizedList'> \| undefined` | VirtualizedList style properties with Abyss token mapping | `-` | No | --- comment: | This is an auto-generated file. DO NOT EDIT THIS FILE DIRECTLY. To edit this file, modify the source file in the 'mobile/overview/' directory, then run the 'copy-abyss-info' script to regenerate this file. slug: /mobile/ui/abyss-info/abyss-overview id: abyss-overview title: Abyss Overview hide_table_of_contents: true --- --- comment: | This is an auto-generated file. DO NOT EDIT THIS FILE DIRECTLY. To edit this file, modify the source file in the 'mobile/overview/' directory, then run the 'copy-abyss-info' script to regenerate this file. slug: /mobile/ui/abyss-info/about id: about title: About Abyss --- ## What is Abyss? ## How Abyss works ## We Support Adoption ## Guiding Principles ## We Maintain Assets ## The Abyss Team --- comment: | This is an auto-generated file. DO NOT EDIT THIS FILE DIRECTLY. To edit this file, modify the source file in the 'mobile/overview/' directory, then run the 'copy-abyss-info' script to regenerate this file. slug: /mobile/ui/abyss-info/abyss-version-2 id: abyss-version-2 title: Abyss Version 2 hide_table_of_contents: true ---
## Abyss Design System version 2 ## V2 prep for designers ## V2 prep for developers ## Stay Connected --- comment: | This is an auto-generated file. DO NOT EDIT THIS FILE DIRECTLY. To edit this file, modify the source file in the 'mobile/overview/' directory, then run the 'copy-abyss-info' script to regenerate this file. slug: /mobile/ui/abyss-info/releases id: releases title: Releases hide_table_of_contents: true ---
--- comment: | This is an auto-generated file. DO NOT EDIT THIS FILE DIRECTLY. To edit this file, modify the source file in the 'mobile/overview/' directory, then run the 'copy-abyss-info' script to regenerate this file. slug: /mobile/ui/abyss-info/contact-us id: contact-us title: Contact Us hide_table_of_contents: true --- ## Support ## Requests --- comment: | This is an auto-generated file. DO NOT EDIT THIS FILE DIRECTLY. To edit this file, modify the source file in the 'mobile/overview/' directory, then run the 'copy-abyss-info' script to regenerate this file. slug: /mobile/ui/abyss-info/disclaimer id: disclaimer title: Mobile Components Disclaimer --- As a note, these components were developed for use in a mobile application environment. For your convenience, we created documentation and interactive examples on our Abyss site. We have tried our best to accurately show the functionality and style of each component on these pages, but you may notice some differences and limitations as the optimal platform for viewing and interacting with these components is a mobile device. A few examples are described below: - [Date Input](/mobile/ui/date-input) requires native code, so this component is only supported on iOS and Android. There are no interactive examples in the documentation; only recordings of each feature are available. To fully interact with the date and time pickers you will need to import the component within a mobile development environment. - [Modal](/mobile/ui/modal), and any component that uses Modal, takes up the entire browser window when opened. While it is intended to take up the entire phone screen as well, the content on the modal will fill much more space when used on a mobile device than it does in the browser window. --- comment: | This is an auto-generated file. DO NOT EDIT THIS FILE DIRECTLY. To edit this file, modify the source file in the 'mobile/overview/' directory, then run the 'copy-abyss-info' script to regenerate this file. id: abyss-overview title: Abyss Overview hide_table_of_contents: true --- --- comment: | This is an auto-generated file. DO NOT EDIT THIS FILE DIRECTLY. To edit this file, modify the source file in the 'mobile/overview/' directory, then run the 'copy-abyss-info' script to regenerate this file. id: about title: About Abyss --- ## What is Abyss? ## How Abyss works ## We Support Adoption ## Guiding Principles ## We Maintain Assets ## The Abyss Team --- comment: | This is an auto-generated file. DO NOT EDIT THIS FILE DIRECTLY. To edit this file, modify the source file in the 'mobile/overview/' directory, then run the 'copy-abyss-info' script to regenerate this file. id: abyss-version-2 title: Abyss Version 2 hide_table_of_contents: true ---
## Abyss Design System version 2 ## V2 prep for designers ## V2 prep for developers ## Stay Connected --- comment: | This is an auto-generated file. DO NOT EDIT THIS FILE DIRECTLY. To edit this file, modify the source file in the 'mobile/overview/' directory, then run the 'copy-abyss-info' script to regenerate this file. id: releases title: Releases hide_table_of_contents: true ---
--- comment: | This is an auto-generated file. DO NOT EDIT THIS FILE DIRECTLY. To edit this file, modify the source file in the 'mobile/overview/' directory, then run the 'copy-abyss-info' script to regenerate this file. id: contact-us title: Contact Us hide_table_of_contents: true --- ## Support ## Requests --- comment: | This is an auto-generated file. DO NOT EDIT THIS FILE DIRECTLY. To edit this file, modify the source file in the 'mobile/overview/' directory, then run the 'copy-abyss-info' script to regenerate this file. id: disclaimer title: Mobile Components Disclaimer --- As a note, these components were developed for use in a mobile application environment. For your convenience, we created documentation and interactive examples on our Abyss site. We have tried our best to accurately show the functionality and style of each component on these pages, but you may notice some differences and limitations as the optimal platform for viewing and interacting with these components is a mobile device. A few examples are described below: - [Date Input](/mobile/ui/date-input) requires native code, so this component is only supported on iOS and Android. There are no interactive examples in the documentation; only recordings of each feature are available. To fully interact with the date and time pickers you will need to import the component within a mobile development environment. - [Modal](/mobile/ui/modal), and any component that uses Modal, takes up the entire browser window when opened. While it is intended to take up the entire phone screen as well, the content on the modal will fill much more space when used on a mobile device than it does in the browser window. --- id: design-checklist title: Design Checklist --- ## Overview Welcome to Abyss! If you're just starting out designing with Abyss, you're in the right place. Here's a checklist of everything you need to get up and running. Abyss design kit is available in Figma through our enterprise account (Optum/UHG). ## Create Figma Account ## Using the Designer Toolkit ## Review Updates --- id: design-kit title: Design Kit --- ## Overview ## Designer Toolkit ## Guidance ## Accessibility ## Contact Us --- id: overview title: Overview --- ## Design resources ## Support --- comment: | This is an auto-generated file. DO NOT EDIT THIS FILE DIRECTLY. To edit this file, modify the source file in the 'mobile/overview/' directory, then run the 'copy-abyss-info' script to regenerate this file. id: abyss-overview title: Abyss Overview hide_table_of_contents: true --- --- comment: | This is an auto-generated file. DO NOT EDIT THIS FILE DIRECTLY. To edit this file, modify the source file in the 'mobile/overview/' directory, then run the 'copy-abyss-info' script to regenerate this file. id: about title: About Abyss --- ## What is Abyss? ## How Abyss works ## We Support Adoption ## Guiding Principles ## We Maintain Assets ## The Abyss Team --- comment: | This is an auto-generated file. DO NOT EDIT THIS FILE DIRECTLY. To edit this file, modify the source file in the 'mobile/overview/' directory, then run the 'copy-abyss-info' script to regenerate this file. id: abyss-version-2 title: Abyss Version 2 hide_table_of_contents: true ---
## Abyss Design System version 2 ## V2 prep for designers ## V2 prep for developers ## Stay Connected --- comment: | This is an auto-generated file. DO NOT EDIT THIS FILE DIRECTLY. To edit this file, modify the source file in the 'mobile/overview/' directory, then run the 'copy-abyss-info' script to regenerate this file. id: releases title: Releases hide_table_of_contents: true ---
--- comment: | This is an auto-generated file. DO NOT EDIT THIS FILE DIRECTLY. To edit this file, modify the source file in the 'mobile/overview/' directory, then run the 'copy-abyss-info' script to regenerate this file. id: contact-us title: Contact Us hide_table_of_contents: true --- ## Support ## Requests --- comment: | This is an auto-generated file. DO NOT EDIT THIS FILE DIRECTLY. To edit this file, modify the source file in the 'mobile/overview/' directory, then run the 'copy-abyss-info' script to regenerate this file. id: disclaimer title: Mobile Components Disclaimer --- As a note, these components were developed for use in a mobile application environment. For your convenience, we created documentation and interactive examples on our Abyss site. We have tried our best to accurately show the functionality and style of each component on these pages, but you may notice some differences and limitations as the optimal platform for viewing and interacting with these components is a mobile device. A few examples are described below: - [Date Input](/mobile/ui/date-input) requires native code, so this component is only supported on iOS and Android. There are no interactive examples in the documentation; only recordings of each feature are available. To fully interact with the date and time pickers you will need to import the component within a mobile development environment. - [Modal](/mobile/ui/modal), and any component that uses Modal, takes up the entire browser window when opened. While it is intended to take up the entire phone screen as well, the content on the modal will fill much more space when used on a mobile device than it does in the browser window. --- id: code-connect title: Code Connect --- ## Figma Code Connect for Mobile :::info Components on the UHC or Global Figma with Code Connect enabled may only be for the V2 version of the component. See the [list below](#supported-components) for supported components. ::: Figma [Code Connect](https://www.figma.com/code-connect-docs/) is a Design-to-Code tool that aims to scaffold out the code required to implement a Figma Design. :::note Code Connect is currently in alpha and availability for components is changing. We invite you to discuss any enhancements or limitations in our [Github Discussion Topic](https://github.com/uhc-tech/abyss/discussions/3775). ::: ## Instructions This [demo video](https://uhgazure.sharepoint.com/teams/AbyssProductUHCProvider/_layouts/15/stream.aspx?id=%2Fteams%2FAbyssProductUHCProvider%2FShared%20Documents%2FHow%20To%20Videos%2Fabyss%2Dcode%2Dconnect%2Dmobile%2Emp4&ga=1&referrer=StreamWebApp%2EWeb&referrerScenario=AddressBarCopied%2Eview%2Ea2480ad0%2D24c0%2D4f27%2Dbecb%2D49e924c87c97) contains an overview of how to use Abyss with Code Connect. - Open the Figma file with the component you want to use and select the component. In dev mode, the Code Connect panel will be viewable in the side bar under "Recommended Code." - The button "Explore component behavior" will allow you to see the component in a preview mode. You can change available props and variants from this panel. :::warning Important Due to Figma limitations, not all possible combinations will be available through Code Connect. Check the Abyss documentation for the full list of available props and variants. ::: ```tsx example () => { return ( Code Connect Example with Badge ); }; ``` #### Slot limitations At this time, code connect does not support slots. If you need to use a slot, you will need to manually add it to the code after copying it from Code Connect. The recommended code section does not show the actual slot element's code. ```tsx example () => { return ( Code Connect Example with Slots ); }; ``` ### Supported Components --- id: abyss-admirals title: Abyss Admirals isHidden: true --- ## Who are Abyss Admirals?
An Abyss Admiral is a highly specialized role for a software engineer who is a dedicated member of a product delivery team. The most basic and essential function of an Admiral is to act as a bridge between the core Abyss ecosystem and the product team leveraging the framework.

Acting as representatives or ambassadors for their products, Admirals enable the adoption of a{' '} scalable, federated software development model by sharing the Abyss community's best practices with their teams. As subject matter experts for Abyss, Admirals are encouraged to guide and mentor their engineering teams, empowering them to take advantage of the benefits of working in a collaborative enterprise environment.
Abyss Admirals
## Benefits for Product Stakeholders It's very important for product stakeholders to understand that an Admiral's involvement in their new responsibilities will reduce their capacity for delivering sprint work as a standard individual contributor. However, by allocating enough time for the role, Admirals will enable engineering scrum teams to measurably improve both quality and delivery metrics. It's recommended to dedicate between **30% - 50%** of an Admiral's capacity for this role, but could be up to 100% depending on the size and scope of the project. Product stakeholders will be able to capitalize on the efficiencies gained by leveraging the collective knowledge and shared solutions that are accessible through the broader Abyss community. The benefits of staffing a dedicated Admiral on your product include: - **Accelerated Solution Development:** When delivery teams are asked to identify and create solutions to common problems, they'll need to do so in between developing new features which can result in delays. An Admiral assists their product teams at critical moments by eliminating these bottlenecks and offering proven solutions, which in turn increases the speed of delivery.

- **Minimized Duplication of Work:** The Abyss team facilitates the creation of reusable digital assets such that, when the business makes a new request, an Admiral can utilize a similar solution that was built previously for another team rather than building a new one from scratch, greatly minimizing cost and time to value.

- **Consistent Product Quality:** It's reasonable to assume that most teams will not be evenly balanced when it comes to experience and skill levels, resulting in products being built with different techniques and standards. Admirals can ensure that the quality of development is both consistent and in accordance with the established standards of other products built with Abyss.

- **Expansive Specialist Network:** When working with an Abyss Admiral, product stakeholders obtain access to a network of highly experienced and qualified specialists including software architects, lead engineers, UX designers, accessibility experts who are motivated to craft the best product experiences possible. ## Benefits for Engineering Managers It's very important for engineering managers to understand that an Admiral's involvement in their new responsibilities will reduce their capacity for delivering sprint work as a standard individual contributor. However, by allocating enough time for the role, Admirals will enable engineering scrum teams to measurably improve both quality and delivery metrics. It's recommended to dedicate between **30% - 50%** of an Admiral's capacity for this role, but could be up to 100% depending on the size and scope of the project. Engineering managers will be able to capitalize on the efficiencies gained by leveraging the collective knowledge and shared solutions that are accessible through the broader Abyss community. The benefits of staffing a dedicated Admiral on your delivery team include: - **Reduced Software Fragmentation:** When individual teams are developing within disconnected, siloed environments, they'll often discover multiple different approaches to solve the same problem. Admirals can act as advisors to prevent this additional overhead from occurring by raising awareness of pre-existing solutions.

- **Promote Engineering Growth:** For an engineer who is eager to progress further along their career path, the Admirals program offers an elevated set of responsibilities for overseeing software projects. Since this role is both highly technical and relationship-oriented, coupled with a sense of personal accountability, Admirals can leverage this experience to explore their interest in management or technology leadership roles.

- **Accountability for Essential Tasks:** Engineering teams are often overburdened with upkeep and maintenance related chores because they are given a lower priority than feature work. By assigning an Admiral to each project, engineering managers can verify that code quality, versioning, and peer review processes are being observed.

- **Optimized Outcomes:** Admirals reduce the time and cost of development through specialization and economies of scale. By tapping into a centralized community of knowledge, skills, and experience, the Admirals program is able to streamline access to those scarce capabilities while also facilitating balanced, cohesive engineering teams. ## Admiral Assignments - **Upgrade Abyss Versions:** It's highly beneficial to keep your product up-to-date with the newest versions of Abyss. Inform your engineering team and product stakeholders of any new components, tools, or patterns your application can leverage. - **Review the [release notes](/mobile/releases/) after a release** to determine the level of effort for upgrading to the latest version. - **Run the command `npm run abyss`** to automatically upgrade all Abyss packages in your project. - **Support for new features and defects** will only be included in new versions.

- **Monitor Code Quality:** As an Admiral, the accountability of maintaining high standards for code quality starts with you. Become well-versed in JavaScript, React, ESLint, and SonarQube anti-patterns and shepherd your team away from these pitfalls, reducing the burden of unrestrained technical debt and extending the lifespan of your codebase. - **Remediate runtime errors & warnings** observed in the browser's developer console for your product. - **Inspect problems reported by [ESLint](https://eslint.org/docs/latest/rules)** and discuss rule modifications with other Admirals. - **Triage issues identified by [Sonar](https://sonar.optum.com)** to ensure your product meets code quality benchmarks.

- **Manage Pull Requests:** Within the GitHub repository for your product, you should encourage your team to open pull requests regularly. By consulting with other Admirals, you are in the most well-suited position to act as a code reviewer for your team. - **Open draft PR's early** in the sprint to give you and your team enough time to review and offer feedback on the approach. - **Offer comments and conduct reviews** for each PR before approving. - **Merge PR's in a timely manner** to improve time-to-build metrics for your product.

- **Leverage Assets:** Admirals should strive to identify all of the usable assets that exist within Abyss, as well as the network of individuals involved. Becoming familiar with the abstract concepts of a framework will elevate the engineering maturity of your team. - **Research code developed for Abyss** to understand the patterns for consistent, repeatable software practices. - **Review and update documentation** which demonstrates guidance for best practices, guidelines, and considerations. - **Foster relationships with key experts** who possess very specific and unique skill sets who can influence the growth of your product.

- **Continuous Learning:** To be successful, Admirals should provide thought leadership, direction, and appropriate recommendations for their teams and the Admiral community. The ability to both absorb and transfer knowledge is essential. - **Have a self-starter attitude** and a passion for growing your career by being surrounded by like-minded engineers. - **Seek opportunities for learning** by reading developer blogs, attending tech conferences, and networking with other Admirals. - **Familiarize yourself with industry trends** by researching and recommending techniques for application development.

- **Sustainable Software:** When left unchecked, the sustainability of an application can continuously deteriorate. Admirals are able to counteract this by taking appropriate measures to establish a healthy development environment and extend the lifespan of a product. - **Maintain a log of tech debt** and track the ongoing scope of maintenance tasks incurred from past sprints. - **Conduct frequent pair programming** sessions with your team to guide current feature development. - **Discuss upcoming requirements** with architects to establish a clear path for future stories in your product pipeline.

- **Abyss Contributions:** With the Admiral contribution process, the development process for new assets can be accelerated by building the solution yourself as the need arises; rather than waiting for your idea to reach the top of the Abyss core backlog. - **Determine the priority** for framework enhancements based on your product delivery schedule. - **Discuss new ideas in [Office Hours](#abyss-office-hours)** with the core team and other Admirals. - **Follow the [Contribution Workflow](#contribution-workflow)** shown below to share your proposals with the framework. ## Admiral Developers Guide If an existing Abyss component doesn't meet your product's requirements, you can follow this guide for building and testing changes within your application's codebase. Start by cloning the package structure of Abyss within your product, such as `src/abyss/mobile/ui/Badge` demonstrated below. If you are creating a new component, you can start with a similar one as a template, otherwise cloning the existing component is the recommended approach. ```txt └── packages └── mobile ├── node_modules ├── src | └── ui | └── Badge | ├── index.js | └── Badge.jsx └── package.json ``` Next, replace the relative imports with absolute paths to `@uhg-abyss/mobile`. You can use any combination of Abyss package imports, open source libraries, and custom JavaScript dependencies to build your component. ```jsx import React from 'react'; import PropTypes from 'prop-types'; import { styled } from '../../tools/styled'; import { useAbyssProps } from '../../hooks/useAbyssProps'; ``` Replace with: ```jsx import React from 'react'; import PropTypes from 'prop-types'; import { styled } from '@uhg-abyss/mobile/tools/styled'; import { useAbyssProps } from '@uhg-abyss/mobile/hooks/useAbyssProps'; ``` Finally, to test your component changes, modify your import path by changing `@uhg-abyss/mobile/ui/Badge` to `@src/abyss/mobile/ui/Badge` which will use your local Abyss component. Once you have fully verified your changes, you can submit a new Pull Request back to [Abyss](https://github.com/uhc-tech/abyss/pulls) and showcase your updates in the Abyss office hours. Once merged, your contributions will be available in the next release! ## Contribution Workflow As an Abyss Admiral the workflow for a contribution goes as follows: 1. Office Hours: Discuss proposal for new components, designs, architecture, and tools with other Admirals. 2. Abyss Contact us: If idea can be re-used, submit a new request with Abyss "Contact Us" form. 3. Develop Locally: Follow the steps in Admiral developers guide to create re-usable asset locally in your product. 4. Abyss GitHub: Before opening a new Pull Request, ensure that all requirements are met for UX, branding, and accessibility guidelines. 5. Abyss Office Hours: Demo proposed feature with Abyss core team and other Admirals. 6. Abyss GitHub: Pull Request undergoes modifications from feedback, acceptance, quality checks and merge. The contribution will end with the finalized Abyss packages ![Contribution Workflow](/img/graphics/abyss_admirals_flowchart.png) ## Abyss Office Hours | Day | Time | Meeting | | --------- | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Tuesdays | 3:00 - 4:00 PM **CST** | [Join Teams Meeting](https://teams.microsoft.com/l/meetup-join/19%3ameeting_YjVlMDM3OTgtY2ExYi00OTU0LWIyYTYtODk0NGUwN2E2MmUz%40thread.v2/0?context=%7b%22Tid%22%3a%22db05faca-c82a-4b9d-b9c5-0f64b6755421%22%2c%22Oid%22%3a%226e73a16f-0cf1-4fd2-9501-31c2c9038e9b%22%7d) | | Thursdays | 9:00 - 10:00 AM **CST** | [Join Teams Meeting](https://teams.microsoft.com/l/meetup-join/19%3ameeting_YjVlMDM3OTgtY2ExYi00OTU0LWIyYTYtODk0NGUwN2E2MmUz%40thread.v2/0?context=%7b%22Tid%22%3a%22db05faca-c82a-4b9d-b9c5-0f64b6755421%22%2c%22Oid%22%3a%226e73a16f-0cf1-4fd2-9501-31c2c9038e9b%22%7d) | --- id: abyss-contributors title: Abyss Contributors --- ## Overview First of all, thank you for your interest in contributing to Abyss. All of your contributions are valuable to the project! There are several ways you can get involved in the Abyss community and become a contributor: - **Share Abyss:** Share the link to [Abyss](https://abyss.uhc.com) with members of your product team, and we'd be happy to discuss how we can help support your application. - **Improve documentation:** Help us improve the [Abyss Docs](https://github.com/uhc-tech/abyss/tree/main/products/abyss-docs-web) by fixing incomplete or missing sections, examples, and explanations. - **Provide feedback:** The team at Abyss are constantly working to make the project better, please let us know what features you would like to see with the [Contact Us](/mobile/contact-us/) form. ## Abyss code repo ```tsx example () => { const packages = [ 'api', 'core', 'desktop', 'ext', 'infra', 'mobile', 'parcels', 'utility', 'web', ]; const products = ['assets', 'docs', 'ext', 'scaffold', 'storybook']; return ( The Abyss source code monorepo contains both core packages and products Packages: {packages.map((item) => { return ( {item} ); })} Products: {products.map((item) => { return ( {item} ); })} {` `} } > Visit ); }; ``` ### Setting up project locally For the essential system tools to get Abyss running on your local development environment, visit our [workplace setup guide](/mobile/developers/workspace-setup). To set up, clone the [Abyss repository](https://github.com/uhc-tech/abyss) to a directory of your choice: ```bash git clone https://github.com/uhc-tech/abyss.git ``` Afterwards, install the dependencies for `abyss` on your machine: ```bash # Go into the abyss directory cd abyss # Install abyss dependencies pnpm i ``` Then you are ready to start `abyss-docs` on your machine: ```bash pnpm run docs ``` ### Commit conventions With several contributors working on Abyss, it's important to write your commit messages to be as descriptive as possible. Abyss follows the conventional commit format to keep commits organized and searchable. ```txt (): [] ``` Examples: ```txt docs(web): Edit Button accessibility section [US123456789] refactor(web): Extract loading overlay logic feat(mobile): Add new Carousel feature [US987654321] fix(docs): Fix docs deployment script [DE1234567] ``` ### Git branch names Naming the branch you're working on helps repository maintainers understand the changes being made when the PR is opened. Using consistent branch name prefixes also allows build tools to automatically categorize the branches using labels. Branch names should be all lowercase (with the exception of `US` and `DE`) and include hyphens between words. All branches are divided into four groups: - **story/** - Changes associated with a User Story, use the unique 7-digit number from Rally followed by a task description. - **defect/** - Changes associated with a Defect, use the unique 7-digit number from Rally followed by a task description. - **refactor/** - Changes to the repo that aren't documented in Rally are considered refactors, so use the task portion to add detail to your branch name. - **release/** - Used specifically by build tools, this branch name is exclusive to release notes and documentation leading up to a new release. Examples: ```bash $ git checkout -b story/US2434515-developer-toolkit $ git checkout -b defect/DE308703-button-accessibility $ git checkout -b refactor/select-input-multi-docs $ git checkout -b story/US1533842-use-loading-overlay ``` Branch Name Rules: - Branch prefix must start with `story/`, `defect/`, `refactor/`, or `release/` - Branch name may consist only of **lowercase letters**, **numbers**, and **hyphens** - The user story or defect ID (`US###` or `DE###`) must be included in the branch name and is an exception to this rule ## Secure groups Visit [Secure](https://secure.uhc.com) to request permissions for the following group: - `abyss_contributors`: For write access to Abyss [GitHub repositories](https://github.com/uhc-tech/abyss) ## Developer tools Abyss is built using a list of trusted resources. Below are links to what makes up the framework of Abyss. ```tsx example () => { const devLinks = [ { name: 'React Native', href: 'https://reactnative.dev/', }, { name: 'Emotion', href: 'https://emotion.sh/docs/introduction', }, { name: 'React Navigation', href: 'https://reactnavigation.org/docs/getting-started', }, { name: 'pnpm', href: 'https://pnpm.io/', }, ]; return ( {devLinks.map((link) => { return ( {link.name} ); })} ); }; ``` If you're ready to get started with Abyss on your own, check out the Abyss StarterKit (coming soon) to get started. ## Design tools Abyss has a dedicated team of designers creating a Design Kit on Figma. Below are some resources to help developers navigate these tools: ```tsx example () => { const designLinks = [ { name: 'Abyss Design Kit', href: 'https://www.figma.com/design/wCMblLsq9TxAQvKzY3EfCt/App--Component-Library-%7C-UHC?node-id=1180-3541', }, { name: 'Figma for developers', href: 'https://www.figma.com/best-practices/tips-on-developer-handoff/an-overview-of-figma-for-developers/', }, { name: 'UHC branding', href: 'https://brand.uhc.com', }, { name: 'Optum branding', href: 'https://brand.optum.com', }, ]; return ( {designLinks.map((link) => { return ( {link.name} ); })} ); }; ``` If you're a designer and want to dive deeper into the Abyss Design Kit, visit our Designer Getting Started (coming soon) page to learn more. --- id: documentation-guide title: Documentation Guide --- ## Overview The documentation pages are organized under the **docs** directory shown below. When adding a new component, tool, or guide to Abyss Docs, create a new markdown.md file under the associated folder. ```txt abyss-docs-web └── docs └── mobile ├── brand ├── developers ├── hooks ├── tools └── ui ``` ## Markdown Structure Each markdown file should begin with the following metadata, as an example: ```md --- id: card category: Layout title: Card description: A single or multi-section container used to display content related to a single subject. design: https://www.figma.com/file/wCMblLsq9TxAQvKzY3EfCt/branch/Sk3MrHYxjT39TKDzDU5LBc/Abyss-Mobile?node-id=12334%3A61355 --- ``` Every doc page is divided into three tabs: Overview, Integration, and Accessibility. Within the body of the markdown file, use these tabs to group sections of information. ```jsx **Overview Content** **Integration Content** **Accessibility Content** ``` ## Overview Tab ###### Import statement Add the import statement for the feature like such: ```jsx import { Alert } from '@uhg-abyss/mobile'; ``` ###### Component Sandbox Add Sandbox after the import statement for any components that make sense to have a sandbox. Inputs are controlled props that can be adjusted by the user using the Sandbox features. Each input contains `prop`, `type` and optionally: `options` and, `defaultValue`. To create a Sandbox, use the convention below: ```tsx example ( Go To Results ); ``` ###### Property examples Following the Sandbox, it's important to show the ability of each property separate of the others. We break each one down, giving it a title, description, and jsx example showing variants of that specific property. For example, if you wanted to show the sizes for Button, you'd write: ```jsx ``` Since there are multiple visual variants of Button, which use the same sizing convention (`small` & `large`) we can combine the visuals under the one size example by organizing them utilizing the built-in Layout component from the Abyss library. Here's what the combined example looks like: ```jsx ``` To follow the complexity of each prop example, use the following rules to properly document the feature: - **When organizing the list of examples,** they should be ordered from simple to complex starting with size or width - **Start each example case** with "Use the `prop-name` property to..." followed by an explanation - **For props with a pre-set list of variants,** add a sentence listing out the variant options "Variants include `variant-1`, `variant-2`," and so on - **For props with a default value,** add "The default value is set to `value`" - **For the customization example section,** include the sentence "If further customization is needed, most styles of `component-name` can be overridden by passing style props to `abyss-component-name`. See the class table on each component for more details" - **Size and width examples** should include the list of Abyss style sizes - **Examples may include:** size, width, isDisabled, controlled, uncontrolled, loading, and customization. Take a look at other doc pages for examples of how to best format the component you're documenting ## Integration Tab Implementing a props table and classes table for the component, and any sub-components gives users an in-depth view of the component without having to visit the code. (The below example is modified for this template. Please refer to the Button component for a full list of props and classes). Follow these rules when creating a Props Table: - **Prop name** is lowercase - **Type** is one of the following: boolean, function, array, shape, number, string, number | string - **Description** first word is uppercase, followed by a brief description of the props use Follow these rules when creating a Classes Table: - **Class name** is lowercase and uses dashes to separate words - **Description** first word is uppercase, followed by a brief description of the class #### Example of Integration Tab ### Button Props ## Button Props | Prop | Type | Description | Default | Required | |------|------|-------------|---------|----------| | `children` | `React.ReactNode \| ((state: PressableStateCallbackType) => React.ReactNode) \| undefined` | The contents of the button component | `-` | No | | `icon` | `IconObj \| IconSymbolName \| IconInsertFunction \| React.ReactNode \| undefined` | Defines the icon name, variant and position | `-` | No | | `iconPosition` | `'leading' \| 'trailing' \| 'iconOnly' \| undefined` | Defines the position of the icon relative to the button text | `'trailing'` | No | | `isDisabled` | `boolean \| undefined` | Disables the button | `false` | No | | `isLoading` | `boolean \| undefined` | Shows a loading spinner | `false` | No | | `onPress` | `Abyss.GestureResponderEventHandler` | Callback fired when the Button is pressed | `-` | No | | `size` | `'large' \| 'small' \| undefined` | Defines the button size | `'large'` | No | | `submit` | `boolean \| undefined` | Flag to indicate the button is a form submit button | `false` | No | | `type` | `'filled' \| 'outline' \| 'text' \| undefined` | Defines the button type (style) | `'filled'` | No | | `variant` | `'brand' \| 'neutral' \| 'destructive' \| 'inverse' \| undefined` | Defines the button variant | `'brand'` | No | ### Button Classes ## Accessibility Tab This tab is important to be as thorough and in-detail as possible, adhering to the WAI-ARIA design guidelines. Check out the accessibility documentation on [React Native](https://reactnative.dev/docs/accessibility) for guidance during development. Follow this pattern when creating the Accessibility tab: - **Brief description** write a description about the component, and link to the WAI-ARIA website page referring to the component - **Sandbox** allows our A11Y partners to practice assistive technology on the component in a dedicated field - **Keyboard interactions table** referring to the WAI-ARIA keyboard interactions, create a table with all interactions usable for the specific component - **Additional guidance** note any additional guidance features of the component, including (but not limited to) Decorative Icons, Loading State, etc. #### Example of Accessibility Tab An alert is an element that displays a brief, important message in a way that attracts the user's attention without interrupting the user's task. Dynamically rendered alerts are automatically announced by most screen readers, and in some operating systems, they may trigger an alert sound. It is important to note that, at this time, screen readers do not inform users of alerts that are present on the page before page load completes. Adheres to the [Alert WAI-ARIA design pattern](https://www.w3.org/TR/wai-aria-practices-1.2/#alert). ```tsx example () => { return ( {}} /> {}} /> {}} /> {}} /> ); }; ```
### Keyboard Interactions ###### Decorative Icons In the alert below, since the word "Warning" appears next to the icon, the icon is considered decorative and must be ignored by assistive technology. The icon does not need to meet the 3:1 minimum contrast requirement against its adjacent color. ```tsx example () => { return ( ); }; ``` ###### Close Button Guidance Keyboard operation: if the "close" button is used on the alert, it must be keyboard accessible. A keyboard only user must be able to tab to the button, and activate it with the space bar and the enter key. ```tsx example () => { return ( {}} /> ); }; ```
Note: per the WAI ARIA specification, when the "alert" role is used, the user should not be required to close the alert. In this case, it is assumed that the close button is provided as a convenience and the user is not explicitly required to close the alert. --- id: mobile-contribution-standards title: Mobile Contribution Standards --- ## Overview Thanks for getting involved with Abyss! If you've made it here we'll assume you've reviewed the [Abyss Contributors](/mobile/developers/contributors/abyss-contributors) page. To make the contribution process go as smooth as possible, we've laid out our code standards and development steps for you below. As a reminder the component you are developing should _already_ be approved by product, design, and accessibility. ## Code Standards While components may differ, most Abyss Mobile UI components have the following... The outermost styled element will be named `ComponentNameRoot`. The rest of the names should describe what they contain.
Below is a simplified example from [Modal](/mobile/ui/modal/) ```
// code
```
The `useAbyssProps` hook must be imported in each component. This allows consuming teams to customize the style of the component and gives the ability to assign unique test-ids. ``` const abyssProps = useAbyssProps(props); ``` `abyssProps` are spread into each element that will allow for customization and testing. The class names start with the component name followed by a dash and then the element name. These names should be simple and self-explanatory. For example, `button-root` describes the root element and `button-label` describes the label element. ``` ```
Add accessibility props when applicable. ``` ```
Don't forget the display name. ``` Button.displayName = '@uhg-abyss/mobile/ui/Button'; ``` ### Types, Props, and Classes All the types, props, and classes used in the component must be defined in a `ComponentName.types.ts` file. This file should export a TypeScript interface for the component's props, classes, and any reused types.
Below is a simplified example of Button: ``` export type ButtonClasses = { 'button-root': Abyss.Class<'Animated.Pressable'>; 'button-label': Abyss.Class<'Animated.Text'>; }; ``` Unless a prop is marked as required in the TypeScript interface, be sure to add a default value where applicable (and within the component itself). ``` export interface ButtonProps extends Abyss.BaseProps { /** * The contents of the button component */ children?: | React.ReactNode | ((state: PressableStateCallbackType) => React.ReactNode); /** * Defines the button type (style) * @default 'filled' */ type?: 'filled' | 'outline' | 'text'; /** * Defines the button size * @default 'large' */ size?: 'large' | 'small'; /** * Disables the button * @default false */ isDisabled?: boolean; /** * Callback fired when the Button is pressed */ onPress?: Abyss.GestureResponderEventHandler; } ``` ``` export interface ButtonRef extends Abyss.PressableRef {} ```
Once the `ComponentName.types.ts` file is complete import the necessary elements into the `ComponentName.tsx` file. ``` import type { ButtonProps, ButtonRef } from './Button.types'; ``` The full props spread may look something like the example below.

`ButtonRoot` is the outermost `Pressable` component, so `size` and `type` are passed in for styling, as well as all the props pertaining to press interactions: `onPress`, `onPressIn`, `onPressOut`, and `disabled`. `{...abyssProps('button-root')}` is added, and for the Button component specifically, accessibility props are only within the `ButtonRoot` element. ``` ``` ## Development Workflow ###### 1. Developer Refinement Before starting development, you are **required** to attend our Mobile Developer refinement session. Here we will discuss the development plan for the component you are contributing. This includes outlining code already available to be used within your component, as well as what we expect to be reusable from your component. ###### 2. Development Please review our [Code Standards](/mobile/developers/contributors/mobile-contribution-standards/#code-standards) before you get started, and remember we're here to help! Feel free to reach out an Abyss Mobile developer and attend [office hours](/mobile/contact-us/?card=meetings) for anything that comes up during development. ###### 3. Documentation A component is not complete without proper documentation. Please see our [Documentation Guide](/mobile/developers/contributors/documentation-guide/) for more details and examples. Be sure to continue to update documentation with any changes from the Peer, QE, and A11y reviews. ###### 4. Peer Review After you've completed development, make a pull request and reach out to the Abyss Mobile developer that has been assigned to review your component. They will do an initial review, as well as check any code changes after QE and Accessibility testing. Any changes requested bring you back to [development](/mobile/developers/contributors/mobile-contribution-standards/#2-development). ###### 5. Quality Engineering Review Once the PR is complete, your component will be sent to our quality engineer for testing. Any changes requested bring you back to [development](/mobile/developers/contributors/mobile-contribution-standards/#2-development). ###### 6. Accessibility Review After your component has been approved by QE, it will be passed on to our accessibility engineer for testing. You are responsible for adding accessibility elements within your component. Check out our [Accessibility Testing](/mobile/developers/testing/accessibility-testing/) page and the [React Native documentation](https://reactnative.dev/docs/accessibility) for further guidance. Any changes requested bring you back to [development](/mobile/developers/contributors/mobile-contribution-standards/#2-development). If the changes made only pertain to accessibility, the component does not need to be reviewed again by QE. ## Definition of Done By contributing to Abyss, you are committing to the full development cycle. A component is complete when all changes have been made and accepted by the Peer, QE, and Accessibility reviews, and is thoroughly documented. --- id: getting-started title: Getting Started --- ## New Application Use [React Native's setup documentation](https://reactnative.dev/docs/environment-setup) to create a boilerplate application, then proceed to the next section to install Abyss. ## Existing Application For teams that want to use Abyss Mobile within an existing React Native installation you can still use Abyss within your application. ### Peer dependencies Please note that React and React Native are peer dependencies, meaning you should ensure they are installed before installing Abyss. :::note Minimum supported versions may change over time, including the React Native version. See the [Versioning Guide](/mobile/developers/versioning-guide#peer-dependency-compatibility) to learn more. ::: ```json "peerDependencies": { "react": "^18.0.0 || ^19.0.0", "react-native": ">=0.81.0 <1", "react-native-safe-area-context": ">=3.0.0", "react-native-screens": ">=2.0.0", "react-native-svg": ">=13.0.0", "@react-navigation/bottom-tabs": ">=6.0.0", "@react-navigation/native": ">=6.0.0", "@react-navigation/native-stack": ">=6.0.0" }, ``` ### Install Abyss Mobile To add Abyss mobile to an existing application, first install the Abyss dependencies: ```jsx npm install @uhg-abyss/mobile ``` Then, wrap your application root component with [`ThemeProvider`](/mobile/theme-customization/tokens/theme-provider). The `ThemeProvider` enables global theming for your application, with the option to customize or rely on the default styles of Abyss components. Utilizing React's context, it distributes your theme to all nested components. ```jsx import { ThemeProvider } from '@uhg-abyss/mobile/ui/ThemeProvider'; const theme = createTheme('uhc'); function Demo() { return ( ); } ``` ## Upgrading Abyss Abyss releases [New Versions](/mobile/releases/) on a biweekly basis. For further details, refer to our [Versioning Guide](/mobile/developers/versioning-guide). You can upgrade Abyss by running the following command in the root of your application: ```bash npm install @uhg-abyss/web@latest ``` ```bash yarn add @uhg-abyss/web@latest ``` Benefits to staying current with the latest version of Abyss include: - **Adhering to Brand Guidelines** - Align with the latest branding guidelines, ensuring your application maintains a consistent look and feel with the overall brand identity. - **Enhanced Security** - Address vulnerabilities and security enhancements to protect your application against emerging threats. - **Improved Accessibility** - As accessibility standards evolve, Abyss updates provide enhancements and fixes that help ensure your application is accessible to all users, including those with disabilities. - **Access to New Components Features** - Gain access to new components and features that can enrich the user experience and offer new functionality for your application. - **Bug Fixes** - Addresses defects that improve the stability and performance of your application. - **Efficient Upgrades and Minimal Regression Testing** - Staying updated with the latest version simplifies the upgrade process and minimizes related regression testing efforts. --- id: v1-to-v2-guide title: V1 to V2 Guide --- Abyss V2 is finally here! We've worked hard to make the transition from V1 as smooth as possible. ## Troubleshooting If you encounter any issues during the migration process, please post your questions, problems, or findings on GitHub Discussions. This will allow all teams to see, respond to, and benefit from shared solutions. If someone has already asked a similar question, consider adding your insights or upvoting the existing discussion rather than creating a duplicate. This helps keep the conversation organized and makes it easier for everyone to find relevant information. [Go to the V1 → V2 Migration Discussion](https://github.com/uhc-tech/abyss/discussions/5083) ## Getting started The steps listed below will guide you through the migration process. ### 1. Update to the latest V1 version Make sure your project is running on the [most recent V1 release](/mobile/releases) before starting any migration steps. This will help minimize potential issues during the migration process. ### 2. Update React In Abyss V2, we have updated our peer dependencies for React and React DOM. Notably, **React 16 and 17 are no longer supported** in Abyss V2. You should ensure your application is running on React 18 or 19 before migrating to Abyss V2. ```json // V1 - "react": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" // V2 + "react": "^18.0.0 || ^19.0.0" ``` ### 3. Update React Native In Abyss V2, we have updated our peer dependencies for React Native. Notably, **React Native versions below 0.70 are no longer supported** in Abyss V2. You should ensure your application is running on React Native 0.70 or higher before migrating to Abyss V2. ```json // V1 - "react-native": ">=0.64.0 <1" // V2 + "react-native": ">=0.70.0 <1" ``` ### 4. Update component usage **It is strongly recommended** to replace your V1 components with their V2 counterparts before updating to Abyss V2. This way, when you move to V2, most of your components will only need to update the import names (e.g., `V2Button` → `Button`) instead of making all the prop changes at the same time. Navigate to the [Component Changes](/mobile/developers/migration/v2/components/) guide to see a list of components with breaking changes between V1 and V2, along with details on what has changed for each component. :::info Some components have breaking changes between V1 and V2 without a V2 counterpart. Please refer to the [Component Migration Notes](/mobile/developers/migration/v2/components#component-migration-notes) section for more details. ::: #### Deprecations Below is a list of tools, hooks, and components that are no longer available in Abyss V2. ### 5. Update to the latest V2 version Now that you've completed all preparation steps, you can update your project to use Abyss V2! Assuming that you have already replaced the deprecated V1 components with their recommended alternatives and switched all other components to their V2 counterparts, the migration to Abyss V2 should be straightforward. Most of the remaining work should be **import renaming** rather than large-scale refactoring. #### Remove the V2 prefix We had released a number of V2 components in V1 with a `V2` prefix to allow teams to start using them early and ease the migration process. Now that you are migrating to Abyss V2, you will need to **remove the `V2` prefix** from these components in your imports. If you used an alias when importing, make sure to update that as well. ```jsx // Before import { V2Button } from '@uhg-abyss/mobile/ui/Button'; import { V2Coachmark as Coachmark } from '@uhg-abyss/mobile/ui/Coachmark'; // After import { Button } from '@uhg-abyss/mobile/ui/Button'; import { Coachmark } from '@uhg-abyss/mobile/ui/TextInput'; ``` #### Updating components with breaking changes As mentioned above some components have breaking changes between V1 and V2 without a V2 counterpart. Please update these components as needed based on the documentation found in the [Component Changes](/mobile/developers/migration/v2/components/) section. Example of a breaking change: ```jsx // V1 (1.X) This is the toast message content // V2 (2.X) ``` ## UHG theme The UHG theme has been **removed in Abyss V2**. To learn more about this change and how to migrate, please navigate to the [UHG Theme](/mobile/developers/migration/v2/v2-uhg-theme) documentation. ## Theming Some values in the overrides provided to the [createTheme function](/mobile/theme-customization/tokens/create-theme) have been removed to better align with brand design guidelines. If your project is using any of the following overrides, you will need to remove them. ### deprecatedOptumIcons The `deprecatedOptumIcons` override has been removed as the old Optum brand icons were not aligned with the Optum brand guidelines. Removing this override will ensure that your application uses the correct icons. No changes to your codebase are necessary beyond removing this override from your theme configuration. :::danger Remove `useDeprecated` prop It was previously possible to override a single `BrandIcon` with the `useDeprecated` prop. This prop is no longer supported in V2 and should be removed from all `BrandIcon` instances. ::: ## AI-Powered Migration Copilot To help with component migration, we provide an AI context package that works with tools like GitHub Copilot, ChatGPT, or other AI code assistants. This context enables the AI to understand V1 to V2 changes and provide accurate migration assistance. #### Using the Migration Copilot - [Download the this zip folder](/migration/mobile/ai-migration-context.zip) - Extract the zip to your project root directory: ``` your-project-root/ ├── abyss-migration-ai-context/ │ ├── ABYSS-MIGRATION-ASSISTANT.md | └── MigrationData.json ├── package.json └── src/ └── ... (your project files) ``` Add the context to your AI tool of choice (e.g., Copilot, ChatGPT, etc.) referencing the folder location. Next you can prompt the AI tool with questions like: ```bash Migrate this file from V1 to V2 of Abyss Migrate the `Button` component from V1 to V2 of Abyss ``` Here is an example showcasing migrating a file from V1 to V2 of Abyss using the AI tool: ```bash Migrate #file:Mobile.tsx from V1 to V2 of Abyss ``` :::note This is the result after using the one prompt above. No additional prompts were needed to get the correct result. ::: ```tsx example () => { return ( AI Migration Example from V1 to V2 of Abyss ); }; ``` --- id: components title: Component Changes --- ## Overview This guide focuses on breaking prop changes to be aware of when migrating from Abyss V1 to V2. These include: - Props that have been removed - Props whose behavior or typings have been updated - Props whose names have been changed but whose functionality remains the same. This guide does **not** cover: - New props added in V2 - Class changes - Token changes - Additional features and enhancements For complete documentation of all available props, including new features added, refer to each component's dedicated documentation page. :::tip AI-Powered Component Migration Need help with component migration? Use our [AI-powered migration tool](/mobile/developers/migration/v2/v1-to-v2-guide#ai-powered-migration-copilot) to help convert V1 components to their V2 equivalents with proper prop mapping. ::: ## Component migration notes While many components in V1 had a dedicated `V2`-prefixed counterpart to allow teams to adopt enhancements gradually, **not all components followed this pattern**. In some cases, breaking changes were introduced directly in the V2 release without creating a separate `V2` component name. To help identify this: - **Migration possible while staying on 1.X — via V2 Component or usage adjustments:** The updated (V2) form of this component is already included in the latest 1.X release. You can migrate without upgrading to 2.X by switching to the V2 Component or updating your usage to avoid deprecated features (such as removing or replacing certain props). - **Requires 2.X upgrade — migration available only in major V2:** The updated component or breaking change is only available starting in major version 2.X, where it becomes the default without any "V2" prefix. ## Badge ## Banner ## Button ## Carousel ## CellGroup ### CellGroup.Cell ## Chip ## Coachmark ## DonutChart ## IconBrand ## LoadingSpinner ## NeedHelp ## Popover ## Rating ## SelectInput ## SelectInputMulti ## Skeleton ## Tabs ## TextField ## TextInput ## Timeline ## Toast --- id: v2-uhg-theme title: UHG Theme --- ## Overview The `uhg` theme will be removed in Abyss V2. **Why is the UHG theme being removed?** The `uhg` theme does not have an official set of design tokens, which means it cannot be aligned to design standards. ## Migration All teams currently using the `uhg` theme should migrate to the `uhc` theme as soon as possible. :::note All actions in this guide can/should be implemented **now** in Abyss V1. ::: The `uhg` and `uhc` themes are very close in appearance. If UHG-specific styling is required, you can override using tokens. **Step-by-step migration instructions are provided below.** **Step 1:** Update the theme used in your application from `uhg` to `uhc`. ```jsx // Old usage const theme = createTheme('uhg'); // New usage const theme = createTheme('uhc'); ``` **Step 2:** If you are using `Brandmark` or `IconBrand` components, update them to use the `brand` prop. ```jsx ``` :::note Teams are welcome to use the `uhc` theme assets (logos, icons) if they prefer the updated branding. ::: **Step 3:** Override tokens (if needed) If you need to preserve specific UHG token styling from V1, you can override tokens when creating your theme. To learn more about overriding tokens, see the [Flatten Tokens](/mobile/theme-customization/tokens/flatten-tokens) and [Create Theme](/mobile/theme-customization/tokens/create-theme) documentation. **Step 4:** Test your application to ensure all components have the desired appearance. As stated above the `uhg` and `uhc` themes are very similar, so minimal changes should be needed. However, it's important to verify that everything looks correct after the migration. :::note Many teams may find the `uhc` theme meets their visual and functional needs without additional customization. Unless your team has specific styling requirements or a business request for a different look, you should expect **little to no changes** beyond the theme switch. ::: ## Smooth Upgrade to V2 By completing the steps outlined above **now** in Abyss V1, you will have already addressed all `uhg` changes. When Abyss V2 is officially released, your application should have **no `uhg`-specific breaking issues**, allowing for a smooth upgrade process. ## Future of the UHG theme There is a possibility that a fully defined UHG theme - with its own complete set of design tokens - may be created in the future. However, this is **not currently planned** and should not be expected in the near term. Teams should use the `uhc` theme moving forward. --- id: legacy-tokens-migration title: Legacy Tokens Migration --- For a long time, Abyss provided a set of "tokens" to allow teams to access standardized values for colors, typography, spacing, and more. These legacy tokens have been **removed** in Abyss V2 in favor of our new [design token system](/mobile/theme-customization/tokens/tokens-reference). Teams that were not using these legacy tokens previously will not need to make any changes to their codebase beyond the normal [component migrations](/mobile/developers/migration/v2/components), but teams that were using them will need to perform some migration steps. The largest challenge is that there is not a one-to-one mapping between the legacy tokens and the new design tokens. To better align with the Abyss V2 Design System, we highly recommend that teams migrate to the new design tokens. However, for teams wanting to minimize up-front work, there is an alternative. Both methods are described below. ## Method 1: Mapping to new design tokens (Preferred) The preferred method for migrating away from legacy tokens is to update your usages of them to the new design tokens. This will ensure your application is fully aligned with the Abyss V2 Design System and will benefit from any future updates to the design tokens. Additionally, as the legacy tokens have been removed, this method will make it easier for the Abyss team to provide support. When migrating to the new design tokens, you will need to identify the appropriate design token that matches the legacy token you were using. Generally speaking, this will mean finding the closest matching [semantic token](/mobile/theme-customization/tokens/tokens-reference?tab=semantic+tokens) and replacing the legacy token with that semantic token. ```jsx Legacy token usage New design token usage ```
:::tip Use semantic tokens While it is possible to use [core tokens](/mobile/theme-customization/tokens/tokens-reference?tab=core+tokens) directly, we strongly recommend using semantic tokens whenever possible. Semantic tokens provide context for how a token should be used, which helps ensure consistency across your application. ::: Additionally, there will likely be some cases where a direct mapping is not possible. In these cases, you will either need to: - Use a hard-coded value that matches the legacy token (not recommended), - Find a semantic token that is close enough, or - Create a custom token in your theme override to match the legacy token (see [Method 2](#method-2-adding-custom-tokens) below). See our [migration table](#legacy-tokens-migration-table) below for help finding the appropriate tokens. ## Method 2: Adding custom tokens The quickest way to migrate to V2 with minimal token changes is to reintroduce any legacy tokens used in your application as custom tokens in your [theme overrides](/mobile/theme-customization/tokens/create-theme#example) in the `createTheme` tool. These tokens will be added into the theme and can be used in the same way as before. The reason we discourage this method is that it adds extra maintenance overhead for your team and is inconsistent with the Abyss V2 Design System. However, it is valid as a temporary solution to minimize up-front work and allow your team to migrate to the new design tokens over time. ## Legacy tokens reference Below are all the legacy tokens we supported in Abyss V1. You can use this as a reference when migrating your application to use the new design tokens. ## Legacy tokens migration table To help with your migration, we've additionally created an interactive table that shows each legacy token, its resolved value, and all matching semantic tokens in the new system. You can click on any token to copy it to your clipboard. :::note Some legacy tokens may not have direct semantic token matches. In these cases, you'll need to either find a semantically similar token, use a core token directly, or add a custom token to your theme. ::: --- id: overview title: Overview --- Abyss is a full-stack mobile application framework that enables you to build products faster and easier than ever. It features a comprehensive set of tools that weaves together the best parts of [React Native](https://reactnative.dev/) and [GraphQL](https://graphql.org). By taking common patterns and modularizing them into accessible and reusable packages, Abyss is designed to accelerate the development of production-ready React Native applications. The framework handles all heavy lifting behind the scenes, allowing you to focus on core business logic specific to your product. Automated code quality tools analyze, identify, and correct errors in the code, giving developers real-time feedback and training to standardize programming styles. With improvements in project maintainability, scalability, and source code quality, Abyss aims to deliver the best overall development experience. Developers looking to use Abyss must have or obtain access to [Artifactory](https://centraluhg.jfrog.io) via [Secure](https://secure.uhc.com/). ## Learning React Native Just starting your journey with React Native? Abyss is a framework built on top of the popular React Native library. To get started, visit the [React Native documentation](https://reactnative.dev/docs/environment-setup). If you are interested in learning high-level concepts, check out the [getting started guide for React Native](https://reactnative.dev/docs/getting-started). ## Developer Tools Abyss is built using a list of trusted resources. Below are links to what makes up the framework of Abyss Mobile. :::info VPN issue Links to the npm site have been blocked on the UHG VPN. To access the npm link below, you will need to disconnect from the VPN. ::: ```tsx example () => { const devLinks = [ { id: 1, name: 'React Native', href: 'https://reactnative.dev/', }, { id: 2, name: 'Emotion', href: 'https://emotion.sh/docs/introduction', }, { id: 3, name: 'React Navigation', href: 'https://reactnavigation.org/docs/getting-started', }, { id: 4, name: 'npm ', href: 'https://docs.npmjs.com/about-npm', }, ]; return ( {devLinks.map((link) => { return ( {link.name} ); })} ); }; ``` ## Support If you're ready to get started with Abyss for your next project, check out our [Contact Us](/mobile/contact-us) page. Submit a new support request and let us know how we can help your team. If you found Abyss to be helpful, please [give us a star on github](https://github.com/uhc-tech/abyss)! --- id: quality-engineering title: Quality Engineering description: QE Testing Overview. --- ## Dedication To Quality ## Test Plan ## Automation Testing Automation testing of Abyss components is a top priority. Currently, our automation tests consist of the following: ### Web ### Mobile ### Unit Testing ## Manual Testing ## FAQ --- id: end-user-spec title: End User Specifications description: Abyss Spec. --- ## Version Requirements ## Testing and Review ## How are these numbers calculated? ## Abyss Web ## Abyss Mobile

\* Last updated March 2025. To ensure this document is kept up to date and relevant, it should be revisited and revised with the latest metrics information on a set schedule, such as quarterly or bi-annually. --- id: component-testing title: Component Testing description: Guide on how to facilitate testing of Abyss Mobile components. --- ## testID To facilitate the usage of component testing libraries such as **[React Native Testing Library](https://callstack.github.io/react-native-testing-library/docs/getting-started)** you have the option of adding a `testID` attribute to a component's corresponding elements. By passing `testID` in as a prop with a value of the desired string id this attribute will be appended to all component elements that include a unique Abyss class name. Please see the Integration tab and the Classes sub-heading for each component to determine which elements will receive this test id. The resulting `testID` value will be a concatenated string that combines the value passed in with the prop and the element's unique class name. For example, the following code: ```tsx example () => { return ( Add your dependents to your account to view their claims and coverage. ); }; ``` will render the following structure: ```jsx Add your dependents to your account to view their claims and coverage. May 13, 2025 ``` ## Change testing strategy By default, the `testID` will have an Abyss class name appended to it. If you do not want this, you can use the [`TestProvider`](/mobile/ui/test-provider) component to change the testing strategy. The `TestProvider` component has two strategies: `"class"` and `"root"`. ### Root strategy The `"root"` strategy applies the `testID` only to the root element of the component. This means you won't be able to target nested elements within the Abyss component for testing. For example, let's say the `testID` of [ProgressBar](/mobile/ui/progress-bar) is set to `"your-test-ID"`. ```jsx ``` You can use the following code to find and test the element: ```jsx import { render } from '@testing-library/react-native'; const screen = render( ); const element = screen.getByTestId('your-test-ID'); ``` ### Class strategy The `"class"` strategy (similar to default) allows testing of nested components by appending the Abyss class name to your `testID`. This creates unique identifiers for each element within the component. For example, let's say the `testID` of [ProgressBar](/mobile/ui/progress-bar) is set to `"your-test-ID"`. ```jsx ``` You will have to look at the classes for the ProgressBar component to determine which elements will receive this test ID and have its class name appended to it. ### ProgressBar Classes The resulting test IDs will be _**`"your-test-ID-abyss-progress-bar-root"`**_ & _**`"your-test-ID-abyss-progress-bar-slide"`**_. You can then use the following code to find and test an element's subcomponents: ```jsx import { render } from '@testing-library/react-native'; const screen = render( ); const element = screen.getByTestId('your-test-ID-abyss-progress-bar-slide'); ``` --- id: accessibility-testing title: Accessibility Testing --- ## Overview Mobile accessibility, also known as A11Y, is the design and creation of mobile applications that can be used by everyone regardless of age, device, or disability. Accessibility support is necessary to allow assistive technology to interpret mobile pages. Abyss fully supports building accessible mobile applications and follows the Web Content Accessibility Guidelines [(WCAG 2.1)](https://www.w3.org/WAI/standards-guidelines/mobile/) and the [UHG Accessibility Engineering Standards](https://uhgazure.sharepoint.com/sites/a11y-engineering-standards) by the Accessibility Center of Excellence (ACOE). The list below are steps to take as a developer to ensure accessibility compliance. Please take a minute to read through the following testing resources and familiarize yourself with how to utilize them for best practices. ## Keyboard Navigation Use an external Bluetooth keyboard only to navigate the page without using your finger to tap or swipe. A visual focus cue, known as the keyboard focus, should appear when navigating content. Look for difficulty reaching or activating interactive components, or if the focus becomes trapped on portions of the screen. Expected keyboard behavior for custom components is typically the following, but there are exceptions - Content navigation - **Tab** to enter a component - Use **arrow keys** to navigate within the component - **Tab** to exit the component - Interacting with CTA or form components - **Enter** or **Space** to mimic a tap or press - **Arrow keys** to navigate to an item within a list OR mimic a swipe - **Tab** to move onto the next item ## Desktop Development Tools XCode's Accessibility Inspector tool is available for inspecting the code of the application for accessibility features and labels via a mobile emulator. ## Screen Reader Mobile Controls A Screen Reader is an accessibility tool used primarily by sight-deficient users to navigate computer/mobile content. They interact with applications by reading aloud the content presented in the code. On mobile devices, users utilize either custom tap/swipe controls, Bluetooth keyboards, or both, to interact with the application. These users are impacted the most from lacking A11Y implementation. Testing with a screen reader on physical mobile devices is important to understand if the code is working effectively for these tools. Mobile devices provide the following screen readers, each with similar, yet unique functionality - iOS VoiceOver: [Learn about VoiceOver gestures](https://support.apple.com/guide/iphone/learn-voiceover-gestures-iph3e2e2281/ios) - Android TalkBack: [Learn about TalkBack gestures](https://support.apple.com/guide/iphone/learn-voiceover-gestures-iph3e2e2281/ios) ## npm Packages Most npm packages rely on axe-core. Set an impact level and start with critical issues then work down. Remember to allow time to fix critical issues in the User Story. Otherwise, the product developers will get frustrated and learn to ignore the errors, which defeats the purpose and doesn't help anyone. ## Linting For linting rules, work with an Accessibility Engineer to determine what to include there. ## Summary Remember, the tools/processes mentioned above don't catch all A11Y issues, but they serve as a great start to empowering the team to do some of your own testing. You can learn more from the Mobile Accessibility page in the [Accessibility Knowledge Center](https://uhgazure.sharepoint.com/sites/accessibility-knowledge-center/SitePages/Mobile-accessibility.aspx). For further information, reach out to an Accessibility Engineer! ## Accessibility Tools If you're looking for an in-depth overview of what accessibility standards Abyss is working towards, visit our Abyss [Accessibility page](/web/resources/accessibility). ```tsx example () => { const accessibilityLinks = [ { id: 1, name: 'WCAG 2.1', href: 'https://www.w3.org/WAI/WCAG21/Understanding/', }, { id: 2, name: 'Color Contrast Analyser (CCA)', href: 'https://webaim.org/resources/contrastchecker/', }, { id: 3, name: 'W3 Validator', href: 'https://validator.w3.org/favelets.html', }, { id: 4, name: 'Digital A11y', href: 'https://www.digitala11y.com/accessibility-bookmarklets-testing/', }, { id: 5, name: 'React Native Accessibility', href: 'https://reactnative.dev/docs/accessibility', }, ]; return ( {accessibilityLinks.map((link) => { return ( } > {link.name} ); })} ); }; ``` --- id: native-liquid-glass-tabs title: Liquid Glass/Native Tabs --- :::tip This is an advanced integration that touches navigation, native modules, and your build config. The file names and paths below are suggestions — adapt them to wherever the equivalent lives in your app. If you get stuck, reach out on our [GitHub Discussions board](https://github.com/uhc-tech/abyss/discussions). ::: ## What you'll build A bottom tab bar that renders the **native iOS 26 Liquid Glass tab bar** on iOS while keeping Abyss' design‑system (JS‑drawn) tab bar on Android — both driven by a **single React Navigation v7** install. - **iOS** → `createNativeBottomTabNavigator` (React Navigation's `/unstable` native tabs, backed by `react-native-screens`). On the iOS 26 SDK this is automatically a Liquid Glass bar, including the isolated search button. - **Android** → Abyss' `createBottomTabNavigator` (the JS‑drawn DS tab bar), which is unchanged. Why split? The native bar gives you the real platform look (Liquid Glass, SF Symbols, the search pill) that can't be reproduced in JS, while Android keeps the Abyss‑branded bar your users already know. :::note Accessibility comes for free Because the bar is the platform's own, it inherits native VoiceOver / TalkBack semantics, Dynamic Type, and reduce‑transparency / contrast handling automatically — no extra work on your side. ::: :::note Requirements This is an advanced integration. Before you start you must be on **React Navigation v7** (see Step 1), **`react-native-screens` 4.25+**, and — for the Liquid Glass appearance specifically — building against the **iOS 26 SDK (Xcode 17+)**. On older SDKs the native bar still works, it just renders as the standard (non‑glass) `UITabBar`. ::: ## Step 1: Move to React Navigation v7 The native tabs API only exists on v7, so the whole app must be on a single v7 install (mixing v6 and v7 is not supported). Below are the changes that affect **Abyss** usage specifically — see the [React Navigation upgrade guide](https://reactnavigation.org/docs/upgrading-from-6.x) for the full list. #### Use the v7‑compatible navigator factories Abyss' `createBottomTabNavigator` and `createStackNavigator` were typed for v6. v7 changed `TypedNavigator` to take a single type‑bag argument, so the v6 types no longer satisfy it. Abyss ships drop‑in v7 casts — switch your imports: ```diff - import { createBottomTabNavigator } from '@uhg-abyss/mobile'; - import { createStackNavigator } from '@uhg-abyss/mobile'; + import { createBottomTabNavigatorV7 } from '@uhg-abyss/mobile'; + import { createStackNavigatorV7 } from '@uhg-abyss/mobile'; ``` ```diff - const Tab = createBottomTabNavigator(); - const Stack = createStackNavigator(); + const Tab = createBottomTabNavigatorV7(); + const Stack = createStackNavigatorV7(); ``` These are runtime‑identical to the v6 factories (same component, same options) — only the types change, so the navigator's screen options and `tabBarIcon`/header callbacks type‑check correctly under v7. ## Step 2: Install dependencies The native tabs live in `@react-navigation/bottom-tabs/unstable` and are backed by `react-native-screens`' native `Tabs` host, which requires **4.25+**. ```bash npm install @react-navigation/native@7.3.3 \ @react-navigation/native-stack@7.17.5 \ @react-navigation/bottom-tabs@7.18.2 \ react-native-screens@4.25.2 \ react-native-safe-area-context@5.8.0 cd ios && pod install ``` Because the native tabs are an **unstable** API, pin to **exact** versions rather than ranges — a minor or patch bump can change the native contract (e.g. renaming a Fabric view manager, which surfaces as a `Can't find ViewManager` crash after a JS-only update). These are the tested versions: | Package | Version | | -------------------------------- | ------- | | `react-native` | 0.86.0 | | `@react-navigation/native` | 7.3.3 | | `@react-navigation/native-stack` | 7.17.5 | | `@react-navigation/bottom-tabs` | 7.18.2 | | `react-native-screens` | 4.25.2 | | `react-native-safe-area-context` | 5.8.0 | :::note TypeScript can't resolve the `/unstable` subpath `@react-navigation/bottom-tabs/unstable` ships its types through the package `exports` map, which many React Native TS setups don't read. If you get a "Cannot find module" error on the import, add an ambient declaration (e.g. in `global.d.ts`) that re‑exports the real types: ```ts declare module '@react-navigation/bottom-tabs/unstable' { export * from '@react-navigation/bottom-tabs/lib/typescript/src/unstable'; } ``` ::: ## Step 3: Split the navigator by platform Create **two** tab navigators from one param list — the native one for iOS and the Abyss one for Android — and pick between them at render time. ```jsx import { Platform } from 'react-native'; import { createBottomTabNavigatorV7 } from '@uhg-abyss/mobile'; import { createNativeBottomTabNavigator } from '@react-navigation/bottom-tabs/unstable'; const isIOS = Platform.OS === 'ios'; type TabParamList = { Home: undefined; Activity: undefined; Profile: undefined; Settings: undefined; }; // iOS -> native UITabBar (React Navigation v7 /unstable), Liquid Glass on iOS 26. // Android -> Abyss' JS-drawn createBottomTabNavigator (design-system tab bar). const NativeTab = createNativeBottomTabNavigator(); const AbyssTab = createBottomTabNavigatorV7(); ``` Render the right one. Because both navigators read the same param list and the same screen components, the only difference is the bar itself: ```jsx function Tabs() { return isIOS ? : ; } ``` The native navigator is plain React Navigation — declare your screens as usual (icons come in [Step 4](#step-4-icons-styling-and-badges)): ```jsx function NativeGlassTabs() { return ( ); } ``` The Android navigator is your existing Abyss tab bar, unchanged apart from the `V7` factory: ```jsx function AbyssJsTabs() { return ( {/* ...the rest of your Abyss tabs */} ); } ``` ## Step 4: Icons, styling, and badges The native navigator doesn't take a React element for `tabBarIcon` — it takes an **icon descriptor**: an **SF Symbol** (iOS) or an **image** (both platforms). A common setup gives iOS an SF Symbol and Android an image via `Platform.select`: ```jsx ``` - **`sfSymbol`** (iOS only) — `{ type: 'sfSymbol', name }`, any name from Apple's [SF Symbols](https://developer.apple.com/sf-symbols/) catalog. - **`image`** (iOS + Android) — `{ type: 'image', source }`. Provide the icon at multiple densities (`icon.png`, `icon@2x.png`, `icon@3x.png`) since native icons aren't auto‑scaled on iOS. You can also point at a platform asset with `source: { uri: 'icon_name' }` (an Android drawable / iOS image set). #### Tinting By default the image is **tinted** with the active/inactive color. On **Android the image is always tinted** (the `tinted` flag is ignored), so a transparent single‑color glyph works best — a flat icon with its own background will tint to a solid block. On **iOS**, pass `tinted: false` to preserve a multi‑color icon's own colors: ```jsx tabBarIcon: { type: 'image', source: require('./assets/tab-icons/home.png'), tinted: false, // iOS: keep the icon's own colors } ``` #### Different icons per state `tabBarIcon` also accepts a function of `{ focused, color, size }` that returns a descriptor — handy for filled/outline variants: ```jsx tabBarIcon: ({ focused }) => ({ type: 'sfSymbol', name: focused ? 'heart.fill' : 'heart', }), ``` This is iOS‑only; on Android the inactive icon is used for both states. #### Styling the bar Native tab bars are intentionally far less able to be styled than the Abyss JS bar — you're getting the platform's own bar, so the OS owns most of the appearance. What you _can_ set per screen: - `tabBarActiveTintColor` / `tabBarInactiveTintColor` — icon and label colors. - `tabBarStyle.display: 'none'` — hide the bar. - `tabBarStyle.backgroundColor` / `shadowColor` — **Android and iOS ≤ 18 only.** The iOS 26 Liquid Glass bar manages its own background and ignores these. There's no equivalent to the JS bar's full style surface (custom item styles, label weights, and so on). If a design needs that level of control, that's a signal to keep the JS bar on that platform. #### Badges Set `tabBarBadge` (a number or string) in a screen's options; `tabBarBadgeStyle` tweaks its appearance: ```jsx ``` ## Step 5: The iOS 26 search tab iOS 26 offers two search affordances. Both are iOS‑only, so gate them with `Platform.select`. #### Isolated search button `tabBarSystemItem: 'search'` renders the search tab as a separate pill on the right of the bar. Android has no equivalent, so fall back to a normal icon + label: ```jsx ``` #### Search‑field transform When the search tab is selected, the bar can morph into a search field. This happens **only** when: 1. the search tab renders a **nested native stack**, and 2. the **focused screen of that stack** sets `headerSearchBarOptions`. It does **not** work if `headerSearchBarOptions` is set on the tab screen itself. Nest a stack instead: ```jsx import { createNativeStackNavigator } from '@react-navigation/native-stack'; const SearchStack = createNativeStackNavigator<{ SearchHome: undefined }>(); function SearchScreen() { return ( ); } ``` …and set the search options from the focused screen: ```jsx function SearchHomeScreen() { const navigation = useNavigation(); const [query, setQuery] = useState(''); useLayoutEffect(() => { navigation.setOptions({ headerSearchBarOptions: { placeholder: 'Search', hideWhenScrolling: false, onChangeText: (e) => setQuery(e.nativeEvent.text), }, }); }, [navigation]); // ...render filtered results, navigate on selection } ``` #### Older iOS (≤ 18) and pre‑iOS‑26 SDK builds Liquid Glass is an iOS 26 feature, opt‑in via the iOS 26 SDK (Xcode 17+). On older OS versions — or when building against an earlier SDK — **the same code renders the standard opaque `UITabBar`**: fully functional, just not glass. No code changes are needed; the fallback is automatic. A few specifics: - The bar is **opaque and part of the layout**, so content sits above it normally — the bottom‑inset handling in [Step 6](#step-6-keep-content-clear-of-the-tab-bar) becomes a no‑op. - `tabBarSystemItem: 'search'` renders an **inline system search tab** (magnifying glass + label) instead of the iOS‑26 isolated pill. - SF Symbol icons still render.
iOS 26 — Liquid Glass tab bar with isolated search pill
iOS 26 — Liquid Glass, isolated search pill
iOS 18 — standard opaque tab bar with inline search tab
iOS 18 — standard UITabBar, inline search
## Step 6: Keep content clear of the tab bar This is the part teams most often miss. The native bar **overlays** the screen — it isn't part of the JS layout, and the JS side can't read its height ([react-native-screens#3627](https://github.com/software-mansion/react-native-screens/issues/3627)). On iOS that's intentional (content scrolls under the translucent glass); the problem is when content gets _clipped_ at the bottom. #### Scrollable content - **iOS** — the native tabs automatically set the content inset on the **first `ScrollView`** found in a screen (the `overrideScrollViewContentInsetAdjustmentBehavior` option, on by default). Usually you do nothing. If your scrollable isn't the first one found, set `contentInsetAdjustmentBehavior="automatic"` on it. - **Android** — there's **no** auto‑inset, and `contentInsetAdjustmentBehavior` is iOS‑only. Inset each tab's whole content area above the bar **once**, using `react-native-screens` experimental `SafeAreaView` with `insetType="interface"`, wired through the navigator's `screenLayout`. Create a small wrapper (e.g. `src/components/NativeTabContent.tsx`): ```jsx import React from 'react'; import { Platform } from 'react-native'; import { SafeAreaView } from 'react-native-screens/experimental'; const isIOS = Platform.OS === 'ios'; export const NativeTabContent = ({ children, }: { children: React.ReactNode, }) => { // iOS scrolls under the glass bar (handled above); Android needs the content inset by the // bar's measured "interface" inset so nothing is clipped. if (isIOS) return <>{children}; return ( {children} ); }; ``` Apply it once on the native navigator — `screenLayout` wraps every screen, so every tab is covered: ```jsx ( {children} )} > {/* ...screens */} ``` :::caution Experimental dependency `react-native-screens/experimental` is explicitly unstable and may change without a major version bump. It's the only way to read the native bar's inset today; pin your `react-native-screens` version, track [react-native-screens#3627](https://github.com/software-mansion/react-native-screens/issues/3627), and swap to a stable `useBottomTabBarHeight()` for native tabs when it lands. ::: #### Absolute / bottom‑anchored content Content that isn't scrollable — a floating button, a `flex-end` button stack — doesn't get the iOS auto‑inset either, so it sits under the glass bar. Wrap it in a small helper: ```jsx import React from 'react'; import { Platform, StyleProp, ViewStyle } from 'react-native'; import { SafeAreaView } from 'react-native-screens/experimental'; const isIOS = Platform.OS === 'ios'; export const TabBarInset = ({ children, style, }: { children: React.ReactNode, style?: StyleProp, }) => { // On iOS, lift content above the native bar. On Android the screen is already inset by // `screenLayout` (above), so this is a no-op there. return ( {children} ); }; ``` Two usage shapes: ```jsx // 1. Wrap an already-positioned overlay (e.g. an absolute floating-button container): // 2. Let it BE the bottom-anchoring container // (replaces a height:'100%' + justifyContent:'flex-end' view): ``` ## Step 7: Fix your test & build config for v7 ESM React Navigation v7 ships as **ESM** with extension-less imports. Anything not told to transpile/relax it will fail on `@react-navigation/*`. Metro handles it out of the box, but **TypeScript**, **Jest**, and **webpack** each need a nudge. **TypeScript** — if your `tsconfig.json` uses a Node‑style `moduleResolution` (`node`, `node16`, or `nodenext`), importing the v7 ESM packages throws `TS1479` (the referenced file "is an ECMAScript module … cannot be imported with `require`"). Switch to bundler resolution — the correct setting for a Metro project anyway: ```json { "compilerOptions": { "module": "esnext", "moduleResolution": "bundler" } } ``` **Jest** — add the navigation packages to the `transformIgnorePatterns` allow‑list (the `(?!…)` negative lookahead) in your Jest config (`jest.config.js`, or the `jest` key in `package.json`): ```js transformIgnorePatterns: [ 'node_modules/(?!((jest-)?react-native|@react-native(-community)?|@react-navigation|react-native-screens|react-native-safe-area-context)/)', ], ``` Without it you'll see `SyntaxError: Cannot use import statement outside a module` (or "unexpected token") pointing at `@react-navigation/native/lib/module/index.js`. **webpack** (Storybook, a docs site, etc.) — relax fully‑specified resolution: ```js module: { rules: [{ test: /\.m?js$/, resolve: { fullySpecified: false } }], }, ``` ## Gotchas & known limitations - **Experimental inset dependency.** `TabBarInset` / `NativeTabContent` rely on `react-native-screens/experimental` — acceptable, but pin `react-native-screens` and track [#3627](https://github.com/software-mansion/react-native-screens/issues/3627). - **The native tabs API is `/unstable`.** It's still maturing; review the open [native bottom-tab issues](https://github.com/react-navigation/react-navigation/issues/12963) (Android especially) before shipping. - **Liquid Glass needs the iOS 26 SDK** (Xcode 17+). On older SDKs the bar renders as a standard `UITabBar` — functional, just not glass. - **You must be fully on v7.** Mixing React Navigation v6 and v7 in one app is unsupported. --- id: versioning-guide title: Versioning Guide --- ## Overview Stability ensures that reusable components and libraries, tutorials, tools, and learned practices don't become obsolete unexpectedly. Stability is essential for the ecosystem around Abyss to thrive. This document contains the practices that are followed to provide you with a leading-edge UI library, balanced with stability, ensuring that future changes are always introduced in a predictable way. ## Semantic versioning Abyss follows [Semantic Versioning 2.0.0](https://semver.org). Abyss version numbers have three parts: major.minor.patch. The version number is incremented based on the level of change included in the release. - **Major releases** contain significant new features, some but minimal developer assistance is expected during the update. When updating to a new major release, you may need to run update scripts, refactor code, run additional tests, and learn new APIs. - **Minor releases** contain important new features. Minor releases should be fully backward-compatible; no developer assistance is expected during update, but you can optionally modify your apps and libraries to begin using new APIs, features, and capabilities that were added in the release. - **Patch releases** are low risk, contain bug fixes and small new features. No developer assistance is expected during update. :::note Abyss component APIs follow semver strictly. React Native is a peer dependency — upgrading it may require separate changes to your app outside of Abyss. [Learn more about supported React Native versions.](#supported-react-native-versions) ::: ## Release frequency A regular schedule of releases helps you plan and coordinate your updates with the continuing evolution of Abyss. In general, you can expect the following release cycle: - A **major** release typically every year for major changes. - A **minor** release every two weeks after each sprint. - A **patch** release at any time for urgent bugfixes. ## Deprecation Practices Sometimes **"breaking changes,"** such as the removal of support for select APIs and features, are necessary. To make these transitions as easy as possible: - The number of breaking changes is minimized, and migration tools provided when possible. - The deprecation policy described below is followed, so that you have time to update your apps to the latest APIs and best practices. ## Deprecation Policy - Deprecated features are announced in the changelog, and when possible, with warnings at runtime. - When a deprecation is announced, recommended update path is provided. - Existing use of a stable API during the deprecation period is supported, so your code will keep working during that period. - Peer dependency updates (React) that require changes to your apps are only made in a major release. ## Supported React Native versions `@uhg-abyss/mobile` follows a **rolling support window**, officially supporting the **latest 5 React Native minor versions** at any given time. See ADR-025 for the full versioning strategy and rationale. ### Why are older versions of React Native dropped? Supporting every React Native version indefinitely grows the testing surface, increases maintenance overhead, and prevents the team from adopting newer APIs and architecture improvements. Dropping older versions on a predictable schedule keeps the library sustainable while giving teams a reasonable window to upgrade. ### Initial adoption To give teams ample time to upgrade, the new minimum of `0.82.0` will be enforced starting approximately **September 1, 2026**. ### Support window Abyss tests against the newest supported React Native version. | React Native | Status | Architecture | | ------------ | -------------- | ------------ | | 0.85.x | ✅ Supported | New | | 0.84.x | ✅ Supported | New | | 0.83.x | ✅ Supported | New | | 0.82.x | ✅ Supported | New | | 0.81.x | ✅ Supported | New / Old | | < 0.81.0 | ❌ Unsupported | — |
:::note Old architecture was removed by default in React Native 0.82. Abyss tests exclusively on the new architecture. ::: ### Peer dependency compatibility | Dependency | Minimum version | | -------------------------------- | --------------- | | `react-native` | `>=0.81.0 <1` | | `react-native-svg` | `>=13.0.0` | | `react-native-video` | `>=6.0.0` | | `react-native-safe-area-context` | `>=3.0.0` | | `react-native-screens` | `>=2.0.0` | | `@react-navigation/native` | `>=6.0.0` | | `@react-navigation/native-stack` | `>=6.0.0` | | `@react-navigation/bottom-tabs` | `>=6.0.0` | --- id: workspace-setup title: Workspace Setup pagination_prev: null --- ## Overview Developing modern JavaScript applications requires efficient, powerful, and extensible tooling. Consistency across developer machines is a priority when collaborating across highly distributed teams. The following is a guide for installing the preferred environment for JS development. ![workspace setup](/img/graphics/workspace.svg) ## Secure Groups Visit [secure.uhc.com](https://secure.uhc.com) to request permissions groups: - **github_users**: To access [github.com](https://github.com) - **Mac_Admin**: To install software for macOS users only ## VSCode Editor To write code for UI projects, it is **highly recommended** that you download and install [Visual Studio Code](https://code.visualstudio.com). ![Visual Studio Code](https://code.visualstudio.com/assets/home/home-screenshot-mac-lg-2x.png) ## VSCode Extensions Recommended extensions will be suggested to you when you visit the VSCode Marketplace. - [ESLint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) - code syntax validator ESLint is a JavaScript linting tool which is used for automatically detecting incorrect patterns found in ECMAScript/JavaScript code. It is used with the purpose of improving code quality, making code more consistent, and avoiding bugs. Rules can be configured to look for all kinds of discrepancies due to discouraged code patterns or formatting. Running a Linting tool over the source code helps to improve the quality and readability of the code. - [Prettier](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) - code formatter Prettier is very popular because it improves code readability and makes the coding style consistent for teams. Developers are more likely to adopt a standard rather than writing their own code style from scratch, so tools like Prettier will make your code look good without you ever having to dabble in the formatting. ## System Essentials ### Xcode - [Xcode Command Line Tools](https://mac.install.guide/commandlinetools/4.html) (Mac Only) `xcode-select` contains necessary utilities for software development on macOS. Xcode's simulator will be used for viewing your app in an iOS environment. ```bash xcode-select --install ```
**_After install, exit, and restart Terminal (CMD + Q)_** ``` $ xcode-select --version ``` ### Android Studio Install [Android Studio.](https://developer.android.com/) The Virtual Device Manager will be used for viewing your app in an Android environment. ### Additional Tools - [oh-my-zsh](https://ohmyz.sh/) >= 5.3.0 (optional) `zsh` is an optional upgrade to the native shell which provides a delightful terminal experience. ```bash sh -c "$(curl -fsSL https://raw.githubusercontent.com/robbyrussell/oh-my-zsh/master/tools/install.sh)" ```
**_After install, exit, and restart Terminal (CMD + Q)_** ```bash omz version ```
--- - [node](https://github.com/nvm-sh/nvm) >= 16.0.0 `nvm` is a great tool for installing and upgrading versions of Node on your system. ```bash curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.1/install.sh | bash ```
**_After install, exit, and restart Terminal (CMD + Q)_** ```bash nvm --version nvm install 16 && nvm use 16 && nvm alias default 16 ```
**_After install, exit, and restart Terminal (CMD + Q)_** ``` $ npm --version $ npm config set registry https://repo1.uhc.com/artifactory/api/npm/npm-virtual ```
--- - [git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) >= 2.0.0 `git` is a universal version control system for working collaboratively and efficiently. ```bash git config --global user.id "YOUR_MS_ID" git config --global user.email "YOUR_EMAIL@optum.com" ``` ## Git Branch Names Naming the branch you're working on helps repository maintainers understand the changes being made when the PR is opened. Using consistent branch name prefixes also allows build tools to automatically categorize the branches using labels. Branch names should be all lowercase (with the exception of US and DE) and include hyphens between words. All branches are divided into four groups: - **story/#######** - Changes associated with a User Story, use the unique 7-digit number from Rally followed by a task description. - **defect/#######** - Changes associated with a Defect, use the unique 7-digit number from Rally followed by a task description. - **refactor/** - Changes to the repo that aren't documented in Rally are considered refactors, so use the task portion to add detail to your branch name. - **release/** - Used specifically by build tools, this branch name is exclusive to release notes and documentation leading up to a new release. Examples: ``` git checkout -b story/US2434515-developer-toolkit git checkout -b defect/DE308703-button-accessibility git checkout -b refactor/select-list-multi-docs git checkout -b story/US1533842-use-loading-overlay ```
Branch Name Rules: - Branch prefix must start with **story**, **defect**, **refactor**, or **release** - Branch name must be only **lowercase letters, numbers, and hyphens** - **US###** and **DE###** are valid character exceptions --- comment: | This is an auto-generated file. DO NOT EDIT THIS FILE DIRECTLY. To edit this file, modify the source file in the 'mobile/overview/' directory, then run the 'copy-abyss-info' script to regenerate this file. id: abyss-overview title: Abyss Overview hide_table_of_contents: true --- --- comment: | This is an auto-generated file. DO NOT EDIT THIS FILE DIRECTLY. To edit this file, modify the source file in the 'mobile/overview/' directory, then run the 'copy-abyss-info' script to regenerate this file. id: about title: About Abyss --- ## What is Abyss? ## How Abyss works ## We Support Adoption ## Guiding Principles ## We Maintain Assets ## The Abyss Team --- comment: | This is an auto-generated file. DO NOT EDIT THIS FILE DIRECTLY. To edit this file, modify the source file in the 'mobile/overview/' directory, then run the 'copy-abyss-info' script to regenerate this file. id: abyss-version-2 title: Abyss Version 2 hide_table_of_contents: true ---
## Abyss Design System version 2 ## V2 prep for designers ## V2 prep for developers ## Stay Connected --- comment: | This is an auto-generated file. DO NOT EDIT THIS FILE DIRECTLY. To edit this file, modify the source file in the 'mobile/overview/' directory, then run the 'copy-abyss-info' script to regenerate this file. id: releases title: Releases hide_table_of_contents: true ---
--- comment: | This is an auto-generated file. DO NOT EDIT THIS FILE DIRECTLY. To edit this file, modify the source file in the 'mobile/overview/' directory, then run the 'copy-abyss-info' script to regenerate this file. id: contact-us title: Contact Us hide_table_of_contents: true --- ## Support ## Requests --- comment: | This is an auto-generated file. DO NOT EDIT THIS FILE DIRECTLY. To edit this file, modify the source file in the 'mobile/overview/' directory, then run the 'copy-abyss-info' script to regenerate this file. id: disclaimer title: Mobile Components Disclaimer --- As a note, these components were developed for use in a mobile application environment. For your convenience, we created documentation and interactive examples on our Abyss site. We have tried our best to accurately show the functionality and style of each component on these pages, but you may notice some differences and limitations as the optimal platform for viewing and interacting with these components is a mobile device. A few examples are described below: - [Date Input](/mobile/ui/date-input) requires native code, so this component is only supported on iOS and Android. There are no interactive examples in the documentation; only recordings of each feature are available. To fully interact with the date and time pickers you will need to import the component within a mobile development environment. - [Modal](/mobile/ui/modal), and any component that uses Modal, takes up the entire browser window when opened. While it is intended to take up the entire phone screen as well, the content on the modal will fill much more space when used on a mobile device than it does in the browser window. --- id: use-animation category: Utilities title: useAnimation description: Use to create standardized animation types for Abyss Mobile components. design: https://www.figma.com/proto/wCMblLsq9TxAQvKzY3EfCt/branch/c6ve0gNrTGpdwBIuJehC3n/Abyss-Mobile?page-id=41951%3A175&type=design&node-id=41951-176&viewport=741%2C524%2C0.06&t=33q3lqs409fRYj6B-1&scaling=scale-down-width&starting-point-node-id=41951%3A176&show-proto-sidebar=1 --- ```jsx import { useAnimation } from '@uhg-abyss/mobile'; ``` ## Usage `useAnimation` hook standardizes the animation types for Abyss components and simplifies additional customization. The hook returns `animate`, `value`, `interpolations`, as well as other [Animated](https://reactnative.dev/docs/animated#methods) functions used throughout Abyss. ```jsx export const useAnimation = (value: number, config: AnimationConfig) => { ... return { animate, value: animatedValue, interpolations, createAnimation, add, subtract, multiply, divide, delay, parallel, loop, sequence, }; }; ``` ## Animate The `animate` function requires a toValue be passed in, with the option for overrides and a callback function. ```jsx const animate = ( toValue: number, configOverride?: Partial, callback?: Animated.EndCallback ) => { return createAnimation(toValue, configOverride).start(callback); }; ``` ```tsx example () => { const { animate, value, interpolations } = useAnimation(1, { easing: 'gentle', interpolations: { background: { inputRange: [0.95, 1], outputRange: [ '$core.color.brand.100', '$semantic.color.surface.container.status.info.saturated', ], tokenType: 'colors', }, }, }); const AnimatedButton = styled('Animated.Pressable', { justifyContent: 'center', alignItems: 'center', padding: '$semantic.spacing.lg', borderRadius: 100, alignSelf: 'center', }); return ( { animate(0.95); }} onPressOut={() => { animate(1); }} style={{ transform: [{ scale: value }], backgroundColor: interpolations.background, }} > Press Me ); }; ``` ### Overrides By default the animation type is set to `'timing'`. Additional types supported are `'spring'` and `'instant'` with the following animation config open to override: ```jsx interface AnimationConfig extends BaseAnimationConfig { type?: 'spring' | 'timing' | 'instant'; interpolations?: Record; delay?: number | undefined; // timing easing?: Easing; duration?: number | undefined; // spring overshootClamping?: boolean | undefined; restDisplacementThreshold?: number | undefined; restSpeedThreshold?: number | undefined; velocity?: number | { x: number, y: number } | undefined; bounciness?: number | undefined; speed?: number | undefined; tension?: number | undefined; friction?: number | undefined; stiffness?: number | undefined; mass?: number | undefined; damping?: number | undefined; } ``` ## Interpolations The `interpolations` prop can take in multiple interpolation objects to be used on the same animated value. ```jsx const { value, animate, interpolations } = useAnimation(1, { easing: 'gentle', interpolations: { backgroundColor: { inputRange: [0.95, 1], outputRange: [ '$semantic.color.surface.container.status.info.saturated', '$core.color.brand.100', ], tokenType: 'colors', }, opacity: { inputRange: [0.95, 1], outputRange: [0, 1], }, }, }); ``` ```tsx example () => { const { animate, value, interpolations } = useAnimation(1, { easing: 'gentle', interpolations: { background: { inputRange: [0.95, 1], outputRange: ['#00BED5', '$core.color.brand.100'], tokenType: 'colors', }, opacity: { inputRange: [0.95, 1], outputRange: [0.8, 1], }, }, }); const Label = styled('Animated.Text', { color: '$semantic.color.text.body.alt', typography: '$semantic.typography.p.xs-bold', marginTop: '$semantic.spacing.xs', }); const AnimatedTabButton = styled('Animated.Pressable', { justifyContent: 'center', alignItems: 'center', padding: '$semantic.spacing.lg', borderRadius: 10, alignSelf: 'center', }); return ( { animate(0.95); }} onPressOut={() => { animate(1); }} style={{ transform: [{ scale: value }], backgroundColor: interpolations.background, }} > ); }; ``` ## Additional Functionality Other Animated methods currently used throughout Abyss components can also be returned. ```jsx const parallel = ( animations: Animated.CompositeAnimation[], parallelConfig?: Animated.ParallelConfig ) => { return Animated.parallel(animations, parallelConfig); }; const loop = ( animation: Animated.CompositeAnimation, loopConfig?: Animated.LoopAnimationConfig ) => { return Animated.loop(animation, loopConfig); }; const sequence = (animations: Animated.CompositeAnimation[]) => { return Animated.sequence(animations); }; const delay = (time: number) => { return Animated.delay(time); }; const add = (num: number) => { return Animated.add(animatedValue, num); }; const subtract = (num: number) => { return Animated.subtract(animatedValue, num); }; const divide = (num: number) => { return Animated.divide(animatedValue, num); }; const multiply = (num: number) => { return Animated.multiply(animatedValue, num); }; ``` ## Accessibility Integration The `useAnimation` hook has built-in integration with the [`useReduceMotion`](/mobile/hooks/use-reduce-motion) hook. This ensures that animated components automatically respect the user's "Reduce Motion" accessibility setting without requiring additional handling. When "Reduce Motion" is enabled, the `useAnimation` hook adjusts the animation type to `'instant'`, minimizing motion effects. This makes it easier to create accessible components that align with user preferences. --- id: use-device-orientation category: Utilities title: useDeviceOrientation description: Used to retrieve the orientation of the mobile device. --- ```jsx import { useDeviceOrientation } from '@uhg-abyss/mobile'; ``` ## Usage `useDeviceOrientation` measures the width and height of the screen and returns `'landscape'` or `'portrait'`. ```jsx const orientation = useDeviceOrientation(); const iconSize = orientation === 'landscape' ? 20 : 24; return ; ``` --- id: use-form category: State Management title: useForm description: useForm is a hook for defining, validating and submitting forms. sourceIsTS: true --- ```jsx import { useForm } from '@uhg-abyss/mobile'; ``` ## Usage The `useForm` hook is used to define, validate, and submit forms in Abyss. Use `useForm` along with the [FormProvider](/mobile/ui/form-provider) component in order to better manage your forms and fully utilize the capabilities of form management within Abyss. Abyss components that can be used with useForm have a `model` prop that is used to bind the component to the form state. Validations can be defined on each component using the `validations` prop, which accepts an object with validation rules. If a component fails validation, it will display an error message based on the validation rules defined. When a form is successfully submitted, the function passed to the `onSubmit` prop will be called with the form data. ```tsx example () => { const form = useForm(); const onSubmit = (data) => { // Do something on submit alert(`FormData: ${JSON.stringify(data)}`); }; return ( ); }; ``` ## Default Values The defaultValues prop populates the entire form with default values. It supports both synchronous and asynchronous assignments of default values. ```tsx example () => { // Default Values Passed into useForm const form = useForm({ defaultValues: { firstName: 'John', lastName: 'Doe', }, }); const onSubmit = (data) => { alert(`FormData: ${JSON.stringify(data)}`); }; return ( ); }; ``` ## Form State This object contains information about the form state. If you want to subscribe to formState via useEffect, make sure that you place the entire formState in the optional array. ```jsx const form = useForm(); const { errors, // An object with field errors isDirty, // Set to true after the user modifies any of the inputs. isValid, // Set to true if the form doesn't have any errors. isValidating, // Set to true during validation. isSubmitting, // true if the form is currently being submitted; false if otherwise. isSubmitted, // Set to true after the form is submitted. isSubmitSuccessful, // Indicate the form was successfully submitted without any Promise rejection or Error being thrown within the handleSubmit callback. submitCount, // Number of times the form was submitted. touchedFields, // An object containing all the inputs the user has interacted with. dirtyFields, // An object with the user-modified fields. } = form.formState; ``` ## Watch This will watch specified inputs and return their values. It is useful for determining what to render. ```tsx example () => { const form = useForm(); const onSubmit = (data) => { alert(`FormData: ${JSON.stringify(data)}`); }; // Watch one field const WatchField = form.watch('firstName'); // Target specific fields by their names const WatchFields = form.watch(['firstName', 'lastName']); // Watch everything by passing no arguments const WatchAllFields = form.watch(); return ( Watch One Field: {JSON.stringify(WatchField)} Watch Multiple Fields: {JSON.stringify(WatchFields)} Watch All Fields: {JSON.stringify(WatchAllFields)} ); }; ``` ## Handle Submit This function will receive the form data if form validation is successful. ```tsx example () => { const form = useForm({ defaultValues: { firstName: 'John', lastName: 'Doe', }, }); const onSubmit = (data) => { alert('onSubmit'); }; const onError = (errors) => { alert('onError'); }; return ( ); }; ``` ## Validate Model This function will receive the model data if form validation is successful. ```tsx example () => { const form = useForm<{ firstName: string; lastName: string }>({ defaultValues: { firstName: 'John', }, }); const handleValidateFirst = () => { form.validate( 'firstName', (data) => { alert(`FormData: ${JSON.stringify(data)}`); }, (error) => { delete error.ref; alert(`Error: ${JSON.stringify(error)}`); } ); }; const handleValidateLast = () => { form.validate( 'lastName', (data) => { alert(`FormData: ${JSON.stringify(data)}`); }, (error) => { delete error.ref; alert(`Error: ${JSON.stringify(error)}`); } ); }; return ( ); }; ``` ## Set Error The function allows you to manually set one or more errors. ```tsx example () => { const form = useForm({ defaultValues: { firstName: 'John', lastName: 'Doe', }, }); // Set single error const setSingleError = () => { form.setError('firstName', { type: 'manual', message: 'There is an error with your name!', }); }; // Set multiple errors const setMultipleErrors = () => { [ { type: 'manual', name: 'firstName', message: 'Check first name', }, { type: 'manual', name: 'lastName', message: 'Check last name', }, ].forEach(({ name, type, message }) => { form.setError(name as 'firstName' | 'lastName', { type, message }); }); }; // Set error for single field errors React.useEffect(() => { form.setError('firstName', { types: { required: 'This is required', minLength: 'This is minLength', }, }); }, []); return ( ); }; ``` ## Clear Errors This function can manually clear errors in the form. ```tsx example () => { const form = useForm({ defaultValues: { firstName: 'John', lastName: 'Doe', phone: '555-555-5555', }, }); const resetErrors = () => { [ { type: 'manual', name: 'firstName', message: 'Required', }, { type: 'manual', name: 'lastName', message: 'Required', }, { type: 'manual', name: 'phone', message: 'Required', }, ].forEach(({ name, type, message }) => { form.setError(name as 'firstName' | 'lastName' | 'phone', { type, message, }); }); }; // Clear single error const clearSingleErrors = () => { form.clearErrors('firstName'); }; // Clear multiple errors const clearMultipleErrors = () => { form.clearErrors(['firstName', 'lastName']); }; // Clear all errors const clearAllErrors = () => { form.clearErrors(); }; return ( ); }; ``` ## Get Values An optimized helper for reading form values. The difference between watch and getValues is that getValues will not trigger re-renders or subscribe to input changes. ```tsx example () => { const form = useForm({ defaultValues: { firstName: 'John', lastName: 'Doe', phone: '555-555-5555', }, }); // Read an individual field value by name const singleValue = form.getValues('firstName'); // Read multiple fields by name const multipleValues = form.getValues(['firstName', 'lastName']); // Reads all form values const allValues = form.getValues(); return (

Single Value: {JSON.stringify(singleValue)}

Multiple Values: {JSON.stringify(multipleValues)}

All Values: {JSON.stringify(allValues)}

); }; ``` ## Trigger Manually triggers form or input validation. This method is also useful when you have dependent validation (input validation depends on another input's value). ```tsx example () => { const form = useForm(); // Trigger one input to validate const triggerSingle = () => { form.trigger('firstName'); }; // Trigger multiple inputs to validate const triggerMultiple = () => { form.trigger(['firstName', 'lastName']); }; // Trigger entire form to validate const triggerAll = () => { form.trigger(); }; const clearErrors = () => { form.clearErrors(); }; return ( ); }; ``` ## Cross-Field Validation Example ```tsx example () => { const form = useForm(); const onSubmit = (data) => { console.log('data', data); }; return ( { const checkValue = form.getValues('middleName-check'); if (!checkValue && !v) { return 'Required'; } }, }} showValidations={false} /> { form.trigger('middleName'); }} /> ); }; ``` ## Additional Documentation `@uhg-abyss/mobile/hooks/useForm` is built on top of the `useForm` hook from [React Hook Form](https://react-hook-form.com/get-started). :::warning Important You should be using Abyss's `useForm` hook when using Abyss components and _not_ React Hook Form's. ::: --- id: use-reduce-motion category: Utilities title: useReduceMotion description: Used within animated components to respect a user's accessibility preferences. --- ```jsx import { useReduceMotion } from '@uhg-abyss/mobile'; ``` ## Usage The `useReduceMotion` hook is a custom hook designed to determine whether the user has enabled the "Reduce Motion" accessibility setting on their device. This setting is often used by individuals who prefer to minimize animations and motion effects for accessibility or comfort reasons. The hook returns an object `{reducedMotionEnabled}` with the boolean value: - `true`: "Reduce Motion" is enabled. - `false`: "Reduce Motion" is disabled. When reduce motion is enabled, this hook can be used to adjust animation behavior. ```tsx example () => { const AnimatedContainer = styled('Animated.View', { padding: '$semantic.spacing.xl', backgroundColor: '$semantic.color.surface.container.primary', width: 100, height: 100, margin: '$semantic.spacing.xl', }); const SequentialAnimation = () => { const { reducedMotionEnabled } = useReduceMotion(); const animatedValues = useRef([ new Animated.Value(1), new Animated.Value(1), new Animated.Value(1), ]).current; useEffect(() => { const createAnimation = (index) => { return Animated.sequence([ Animated.timing(animatedValues[index], { toValue: 1.4, duration: 500, useNativeDriver: false, }), Animated.timing(animatedValues[index], { toValue: 1, duration: 500, useNativeDriver: false, }), ]); }; const loopAnimation = Animated.loop( Animated.stagger(250, [ createAnimation(0), createAnimation(1), createAnimation(2), ]) ); loopAnimation.start(); return () => { return loopAnimation.stop(); }; }, []); return ( {animatedValues.map((animatedValue, index) => { return ( ); })} ); }; return ; }; ``` ## Enabling Reduce Motion by Device ### iOS - Open the Settings app - Select Accessibility - Choose Motion - Toggle the switch next to Reduce Motion to on ### Android - Open the Settings app - Select Accessibility - Depending on your Android version, look for Remove Animations (or similar) ### Windows - Open Settings - Select Accessibility - Go to Visual Effects - Toggle Animation Effects to off ### Mac OS - Open System Settings - Select Accessibility - Choose Display - Toggle Reduce Motion to on ## Additional Notes - The hook listens for changes to the "Reduce Motion" setting and updates its value dynamically. - The [`useAnimation`](/mobile/hooks/use-animation) hook integrates `useReduceMotion` directly, ensuring that animated components automatically respect the user's motion preferences without requiring additional handling. - Use this hook to create a more inclusive and accessible experience for users who prefer minimal motion effects. Please refer to accessibility guidelines for animations when creating new components. - WCAG standards: - [iOS](https://uhgazure.sharepoint.com/sites/a11y-engineering-standards/SitePages/CP-2.33-Animations-from-Interactions-Native-iOS.aspx?web=1) - [Android](https://uhgazure.sharepoint.com/sites/a11y-engineering-standards/SitePages/CP-2.33-Animations-from-Interactions-Native-Android.aspx?web=1) --- id: use-set-focus category: Utilities title: useSetFocus description: Used to set accessibility focus on a specified element. --- ```jsx import { useSetFocus } from '@uhg-abyss/mobile'; ``` ## Usage The hook returns a function that consumes a ref object. Call the function to move screen reader focus to the target element. :::warning For accessibility focus to work correctly, you must provide `accessible={true}` on the element that needs to be focused. This ensures the element is recognized by screen readers. ::: ```jsx const setFocus = useSetFocus(); const myRef = useRef(null); return ( <> Link to be focused ); ``` ## Example In the example below, Android's Talkback can be seen focusing the bottom text when the `Focus Text` button is pressed. --- id: use-translate category: Utilities title: useTranslate description: Used to get the translated string from the i18n object. --- ```jsx import { useTranslate } from '@uhg-abyss/mobile/hooks/useTranslate'; ``` The `useTranslate` hook is used to get the translated string from the Abyss [i18n](https://github.com/uhc-tech/abyss/blob/main/packages/abyss-mobile/src/tools/i18n/translations/en.ts) object. ## Usage ```typescript interface I18nTranslate { t: (key: string, replacements?: object) => string; i18n: object; } useTranslate(key: string, replacements?: object): I18nTranslate ``` The `key` argument corresponds to the key in the i18n object. The `replacements` argument is an object that contains the values to replace in the translated string. Let's use an example to illustrate how to use the `useTranslate` hook. In the [TextArea](/mobile/ui/text-area) component, we have a text block that displays the remaining characters available to be typed in the text area. We can get that value with the key `TextArea.charactersRemaining`. ```tsx example () => { const { t } = useTranslate(); return {t('TextArea.charactersRemaining')}; }; ``` If we want to replace the value of the remaining characters, we can pass in the `replacements` object with the key `count`. Replacement values should always appear in double curly braces, (e.g. `{{count}}`). ```tsx example () => { const { t } = useTranslate(); return {t('TextArea.charactersRemaining', { count: 10 })}; }; ``` We can also use the `useTranslate` hook to get the translated string from the i18n object. ```tsx example () => { const { i18n } = useTranslate(); return {i18n.TextArea.charactersRemaining}; }; ``` --- id: abyss-overview slug: /mobile/abyss-overview title: Abyss Overview hide_table_of_contents: true --- --- id: about slug: /mobile/about title: About Abyss --- ## What is Abyss? ## How Abyss works ## We Support Adoption ## Guiding Principles ## We Maintain Assets ## The Abyss Team --- id: abyss-version-2 slug: /mobile/abyss-version-2 title: Abyss Version 2 hide_table_of_contents: true ---
## Abyss Design System version 2 ## V2 prep for designers ## V2 prep for developers ## Stay Connected --- id: releases slug: /mobile/releases title: Releases hide_table_of_contents: true ---
--- id: contact-us slug: /mobile/contact-us title: Contact Us hide_table_of_contents: true --- ## Support ## Requests --- id: disclaimer slug: /mobile/disclaimer title: Mobile Components Disclaimer --- As a note, these components were developed for use in a mobile application environment. For your convenience, we created documentation and interactive examples on our Abyss site. We have tried our best to accurately show the functionality and style of each component on these pages, but you may notice some differences and limitations as the optimal platform for viewing and interacting with these components is a mobile device. A few examples are described below: - [Date Input](/mobile/ui/date-input) requires native code, so this component is only supported on iOS and Android. There are no interactive examples in the documentation; only recordings of each feature are available. To fully interact with the date and time pickers you will need to import the component within a mobile development environment. - [Modal](/mobile/ui/modal), and any component that uses Modal, takes up the entire browser window when opened. While it is intended to take up the entire phone screen as well, the content on the modal will fill much more space when used on a mobile device than it does in the browser window. --- id: product-resources title: Product Resources --- ## Overview ## How does Abyss work? ## Versioning ## Branding ## Accessibility ## Support --- id: product-inclusion title: Product Inclusion --- ## Mission statement ## Product inclusion principles ## Product inclusion checklist ## Product inclusion audit tool ## Contact us --- id: accessibility title: Accessibility --- ## Overview ## Interactive components ```tsx example () => { return ; }; ``` ## Color Contrast ```tsx example () => { return (