Accessibility warnings on Ionic elements by Google Play pre-launch report

Hello team!

Submitting my Angular 15 + Ionic 6.20.1 app for pre-launch review in the Google Console, I received a pre-launch report with 35 Touch target size issues and 9 Low color contrast issues.

In particular, I’m curious about the 35 Touch target size issues, however classified as minor issues, as these are totally controlled by Ionic and I would not expect these to be misaligned with Google recommendations.

Example of such issues:

Recommendation

Consider making this clickable item larger.

This item's size is 32dp x 32dp. Consider making this touch target 48dp wide and 48dp high or larger. This item originates from web content, and additional verification of touch target size is recommended.

This is a standard input control of Ionic, generated with this code:

<ion-item class="input-item input-item-text">            
       <ion-input label="Name" labelPlacement="floating" type="text" formControlName="name" autofocus="true" clearInput #userName></ion-input>
</ion-item>

Another example:

Recommendation

Consider making this clickable item larger.

This item's height is 36dp. Consider making the height of this touch target 48dp or larger. This item originates from web content, and additional verification of touch target size is recommended.

This Select drop-down is generated with this code:

<ion-item class="input-item input-item-text item-label-floating">
            <ion-label position="floating">Country</ion-label>
            <ion-select formControlName="country" placeholder="Select your country" interface="popover"
              cancelText="Cancel" okText="OK" (ionChange)="onCountryChange($event)">
              <ion-select-option *ngFor="let item of countries" [value]="item" class="alert-radio-label">{{item.name}}
              </ion-select-option>
            </ion-select>
            <ion-thumbnail slot="end">
              <ion-img [src]='selectedCountryFlag' style="width: 16px;height:11px; margin-top:70%; margin-left:70%;">
              </ion-img>
            </ion-thumbnail>
</ion-item>

Another example:

Recommendation

Consider making this clickable item larger.

This item's height is 45dp. Consider making the height of this touch target 48dp or larger. This item originates from web content, and additional verification of touch target size is recommended.

Another example:

Consider making this clickable item larger.

This item's size is 40dp x 34dp. Consider making this touch target 48dp wide and 48dp high or larger. This item originates from web content, and additional verification of touch target size is recommended.

This last example is generated by this code:

<ion-header>
  <ion-toolbar>
    <ion-buttons slot="start">
      <ion-button (click)="navigateBack()">
        <ion-icon name="chevron-back-outline" slot="icon-only"></ion-icon>
      </ion-button>
    </ion-buttons>
    <ion-title>{{this.pageTitle}}</ion-title>
  </ion-toolbar>
</ion-header>

As you can see, this is standard Ionic use of the back button element.

Please advise why are these issues being raised and what can I do (if at all on my end) to fix?

Out of curiosity, why are you using the iOS theme on Android? Using the Material Design theme follows Google’s design system which should address many of those warnings on Android.

Hi Idebeasi.
Not sure I follow you, I must be missing something.

I did not chose any theme. I am also using the default scss settings (colors) out of the box.

What hinted you of this “iOS theme” in my message?
How is this theme selected?

I am using Ionic to be able to work transparently with both Android and iOS out of the box, without the need to chose any thing specific.

What am I missing, please?

Thank you!
Mor

Your original post noted that Google Play reported accessibility warnings, which means this was coming from your Android application. However, your screenshots show the iOS mode which is typically used on iOS devices, not Android devices. You might want to try using the Material Design theme on Android instead since that follows Google’s design language.

Ionic automatically chooses the theme based on platform, but it looks like something is causing the iOS theme to be selected on Android devices. I’d verify that the Material Design theme is being used for your Android app.

Thanks.

I cannot see any hint why that would be so.
Here my ionic.config file:

{
  "name": "Simplify",
  "integrations": {
    "capacitor": {}
  },
  "type": "angular"
}

The component where these warnings are coming from, has the following scss entries, I did not think they can switch the mode for the whole component. Do you see any reason for that?

@import "../../../theme/mixins/inputs/select-alert";

.form-card {
  border-radius: 10px;
  display: block;
  width: 90%;
  //box-shadow: -1px 3px 5px 0px rgba(0,0,0,0.75);
  margin: auto;
  margin-top: 30px;
  margin-bottom: 90px;
  overflow: visible;
  padding-left: 12px;
  padding-right: 12px;
  padding-bottom: 10px;
}

.link-device-hyperlink a {
  font-size: 80%;
  text-align: center !important;
}

.terms-button,
.privacy-button {
  //background-color:transparent;
  border: none;
  color: blue;
  //padding: 5px 10px;
  text-align: center;
  display: inline-block;
  margin-top: 6px;
  //margin-left: -2px;
  cursor: pointer;
}

.terms-button {
  width: 40px;
}

.privacy-button {
  width: 90px;
}

.forgot-password-button {
  border: none;
  color: blue;
  display: inline-block;
  margin-top: 6px;
  cursor: pointer;
}

.terms-button span,
.privacy-button span {
  text-transform: none;
  font-size: 90%;
}

.explanation-note {
  font-style: italic;
  padding-top: 5px;
}

// Note:  All the CSS variables defined below are overrides of Ionic elements CSS Custom Properties
.forms-validations-content {
  --background: var(--page-background);

  .validations-form {
    margin-bottom: calc(var(--page-margin) * 2);

    .inputs-list {
      --ion-item-background: var(--page-background);

      padding: var(--page-margin) var(--page-margin) calc(var(--page-margin) * 2);

      ion-list-header {
        padding-inline-start: 0px;

        .header-title {
          text-transform: uppercase;
          font-size: 16px;
          color: var(--ion-color-secondary);
        }
      }

      .input-item {
        --padding-start: 0px;
        --padding-end: 0px;
        --inner-padding-end: 0px;
      }

      .input-item-text {
        background-color: #eaeaea !important;
        font-style: italic;
      }

      .terms-item {
        --border-width: 0px;
        --inner-padding-end: 0px;
        --inner-padding-start: 0px;
        font-size: 80%;
        margin-top: 10px;
        margin-bottom: 20px;
      }

      .error-container {
        .error-message {
          margin: calc(var(--page-margin) / 2) 0px;
          display: flex;
          align-items: center;
          color: var(--ion-color-danger);
          font-size: 14px;

          ion-icon {
            padding-inline-end: calc(var(--page-margin) / 2);
            flex-shrink: 0;
          }
        }
      }

      .counter-item {
        app-counter-input {
          --counter-background: transparent;
          --counter-color: var(--ion-color-primary);
          --counter-border-color: var(--ion-color-primary);
        }

        .counter-value {
          text-align: center;
        }
      }
    }

    .submit-btn {
      margin: var(--page-margin);
      margin-top: 15px;
    }

    .cancel-btn {
      margin-top: 15px;
    }
    .next-btn {
      margin-top: 15px;
    }
  }
}

.terms-checkbox {
  margin-right: 10px;
  width: auto;
}


#password-item {
  color: orange;
}

#term-header {
  margin-top: 25px;
}


// Alerts and in general all overlays are attached to the body or ion-app directly
// We need to use ::ng-deep to access it from here
::ng-deep .select-alert {
  @include select-alert();

  // Variables should be in a deeper selector or after the mixin include to override default values
  --select-alert-color: var(--ion-color-lightest);
  --select-alert-background: var(--ion-color-primary);
  --select-alert-margin: 16px;

  .alert-message {
    display: none;
  }

  .alert-input-numeric {
    font-size: 400%;
    text-align: center;
  }
}


ion-radio {
  --border-radius: 4px;
  --inner-border-radius: 4px;

  --color: #ddd;
  --color-checked: var(--ion-color-primary);
}

ion-radio.ios {
  width: 20px;
  height: 20px;

  border: 2px solid #ddd;
  border-radius: 4px;
}

.radio-checked.ios {
  border-color: var(--ion-color-primary);
}

.recovery-option-body {
  margin-left: 15px;
}

.recovery-option-item {
  margin-left: -15px;
}


.alert-ios .alert-radio-label{
  white-space: pre-line !important;
  }
  
.alert-md .alert-radio-label{
  white-space: pre-line !important;
  }
  
.alert-wp .alert-radio-label{
  white-space: pre-line !important;
  }

  .recovery-option-label,
  .item.sc-ion-label-ios-h,
  .item.sc-ion-label-md-h,
  .item.sc-ion-select-popover-ios,
  .item.sc-ion-label-ios-s
   {
      white-space: normal !important;
    }

  .ion-text-wrap {
    white-space: normal !important;
  }

And this is my global.scss:

/*
 * App Global CSS
 * ----------------------------------------------------------------------------
 * Put style rules here that you want to apply globally. These styles are for
 * the entire app and not just one component. Additionally, this file can be
 * used as an entry point to import other CSS/Sass files to be included in the
 * output CSS.
 * For more information on global stylesheets, visit the documentation:
 * https://ionicframework.com/docs/layout/global-stylesheets
 */

/* Core CSS required for Ionic components to work properly */
@import "~@ionic/angular/css/core.css";

/* Basic CSS for apps built with Ionic */
@import "~@ionic/angular/css/normalize.css";
@import "~@ionic/angular/css/structure.css";
@import "~@ionic/angular/css/typography.css";
@import '~@ionic/angular/css/display.css';

/* Optional CSS utils that can be commented out */
@import "~@ionic/angular/css/padding.css";
@import "~@ionic/angular/css/float-elements.css";
@import "~@ionic/angular/css/text-alignment.css";
@import "~@ionic/angular/css/text-transformation.css";
@import "~@ionic/angular/css/flex-utils.css";

/* Simplify custom styles */

.toast-success {
    --color:#006100;
    --border-color:#006100;
    --border-radius:3px;
    --border-style:solid;
    --border-width:3px;    
    --background:#C6EFCE;
    /*--button-color:#ffffff;*/
}

.toast-warning {
    --color:#9C5700;
    --border-color:#9C5700;
    --border-radius:3px;
    --border-style:solid;
    --border-width:3px;    
    --background:#FFEB9C;
    /*--button-color:#ffffff;*/
}

.toast-error {
    --color:#9C0006;
    --border-color:#9C0006;
    --border-radius:3px;
    --border-style:solid;
    --border-width:3px;    
    --background:#FFC7CE;
    /*--button-color:#ffffff;*/
}

.responses-instructions-modal {
    --background: transparent;
}

//As we defined the global background variable to be transparent (for the camera-preview in home-page), we force an opaque background for elements inheriting the transparent background
.alert-wrapper {
    --background: black;
}

.alert-title {
    color:white !important;
}

.alert-message {
    color:white !important;
}

.alert-input-numeric {
    font-size: 400% !important;
    text-align: center !important;
}

.alert-input-text {    
    text-align: center !important;
}


body.camera-active {
    --background: transparent;
    --ion-background-color: transparent;
  }

  /* User Input component modal css */
.user-input-modal-css {
    height: 50%;
    width: 80%;
    top: 25%;
    left: 10%;
    position: absolute; 
    display: block;
    background-color: antiquewhite;
    --backdrop-opacity: var(--ion-backdrop-opacity, 0.1);
}

 /* wrap ion-select options in drop-down */


 sc-ion-select-popover-ios sc-ion-label-ios-h sc-ion-label-ios-s ios hydrated

.recovery-option-label,
.item.sc-ion-label-ios-h,
.item.sc-ion-label-md-h,
.item.sc-ion-select-popover-ios,
.item.sc-ion-label-ios-s
 {
    white-space: normal !important;
  }
 
 .alert-radio-label.sc-ion-alert-md,
 .alert-radio-label.sc-ion-alert-ios {
    white-space: normal !important;
 }
 
 
 .alert-tappable.alert-radio {
    height: auto !important;
    contain: content !important;
 }
 
 
 .item.sc-ion-label-md-h, .item .sc-ion-label-md-h{
    white-space: normal !important;
}

.sc-ion-label-md-h{
    white-space: normal !important;
}

.alert-checkbox-label{
    white-space: normal !important;
  }

  .alert-radio-label{
    white-space: normal !important;
  }
  
.alert-tappable.sc-ion-alert-md,
.alert-tappable.sc-ion-alert-ios {
    height: initial !important;
    contain: initial !important;
  }

.alert-ios .alert-radio-label {
    white-space: pre-line !important;
}
.alert-md .alert-radio-label {
    white-space: pre-line !important;
}
.alert-wp .alert-radio-label {
    white-space: pre-line !important;
}

What device did you take the screenshots on?

Also make sure that your IonicModule.forRoot does not have mode: 'ios'.

This is it!
I just found this in the app.module file:

IonicModule.forRoot({ mode: "ios" }),

Have no idea why it is there.
Can I safely remove it?

Those screenshots were taken by Google’s automatic pre-launch testing programs, not by myself.

You can remove that. By any chance did you create your application based off a StackBlitz playground? (i.e. something like this ion-alert: Ionic API Alert Buttons with Custom Message Prompts)

I will remove.

No, did not create the app like that. Mostly just manual coding from a blank project.

Thanks!
Mor

1 Like