PingOne Recognize

User interface customization

You can use the Web SDK to customize the appearance of PingOne Recognize user interface (UI) components by setting property values.

Customizing user interface elements

Update the theme-options or themeOptions attributes with an object using the following structure:

Details
interface ThemeOptions {
  colors: {
    dark: {
      primary: string
      onPrimary: string
      secondary: string
      onSecondary: string
      secondaryContainer: string
      onSecondaryContainer: string
      surface: string
      onSurface: string
      surfaceVariant: string
      onSurfaceVariant: string
    }
    light: {
      primary: string
      onPrimary: string
      secondary: string
      onSecondary: string
      secondaryContainer: string
      onSecondaryContainer: string
      surface: string
      onSurface: string
      surfaceVariant: string
      onSurfaceVariant: string
    }
  }
  elements: {
    button: {
      host: {
        borderRadius: string
        fontSize: string
        fontWeight: string
        padding: string
      }
      size: {
        small: {
          host: {
            fontSize: string
            padding: string
          }
        }
      }
      variant: {
        text: {
          host: {
            borderBottom: string
            hover: {
              opacity: string
            }
          }
        }
      }
    }
    camera: {
      host: {
        after: {
          background: string
          height: string
        }
        before: {
          background: string
          height: string
        }
      }
    }
    cameraCorners: {
      svg: {
        strokeWidth: string
      }
    }
    cameraInstructions: {
      host: {
        gap: string
      }
      li: {
        borderRadius: string
        gap: string
        padding: string
      }
      liText: {
        fontSize: string
      }
    }
    cameraSelect: {
      labels: {
        gap: string
        padding: string
      }
      labelsHeadline: {
        fontSize: string
        fontWeight: string
      }
      labelsText: {
        fontSize: string
        fontWeight: string
      }
      list: {
        borderRadius: string
        margin: string
        padding: string
        top: string
      }
      option: {
        borderRadius: string
        marginTop: string
        padding: string
      }
    }
    dialog: {
      host: {
        border: string
        borderRadius: string
        boxShadow: string
      }
    }
    poweredBy: {
      host: {
        gap: string
        height: string
      }
      icon: {
        height: string
        width: string
      }
      span: {
        fontSize: string
        fontWeight: string
        letterSpacing: string
      }
    }
    qrcode: {
      host: {
        borderRadius: string
        padding: string
      }
    }
    root: {
      buttonCameraSelect: {
        right: string
        top: string
      }
      buttonCancel: {
        left: string
        top: string
      }
      buttonClose: {
        left: string
        top: string
      }
      buttonFlash: {
        right: string
        top: string
      }
      buttonPin: {
        height: string
        width: string
      }
      buttonsSwitchToMobileChoice: {
        gap: string
      }
      cameraBiometric: {
        width: string
      }
      cameraTip: {
        backdropFilter: string
        borderRadius: string
        fontSize: string
        fontWeight: string
        height: string
        padding: string
        top: string
      }
      headline: {
        fontSize: string
        fontWeight: string
        marginTop: string
      }
      host: {
        borderRadius: string
        gap: string
        padding: string
      }
      poweredBy: {
        bottom: string
      }
      text: {
        fontSize: string
        fontWeight: string
      }
      texts: {
        gap: string
      }
    }
  }
}

Interface structure

The theme options structure includes two main properties, colors and elements.

The colors block supports dark mode settings by providing dark and light objects, which each define color settings for the corresponding display mode.

The following diagram shows how each sub-property applies to the user interface.

Annotated screenshot of the authentication error screen showing how color theme properties map to UI elements. The card background maps to the surface property. The card border maps to surfaceVariant. The error animation icon (a red circle with an X) is labeled as the Lottie slot. The heading 'Something went wrong' maps to onSurface, the primary body text color. The error description text maps to onSurfaceVariant, the secondary body text color. The Retry button background maps to primary, the button and spinner color. The button label text maps to onPrimary, the button text color.

The properties of the elements object affect the corresponding UI elements. Each property corresponds to a specific user interface element except for the ae property, which refers to the abstraction layer behind the authentication (a) and enroll (e) operations.

Style changes applied to an element property affect the corresponding user interface element.

Default style values

The following example shows the default style values for PingOne Recognize:

Details
{
  colors: {
    dark: {
      primary: '#1833B8',
      onPrimary: '#FFFFFF',
      secondary: '#FFD900',
      onSecondary: '#1A1A1A',
      error: '#BA3B1B',
      secondaryContainer: '#2B2B2B',
      onSecondaryContainer: '#F8F8F8',
      surface: '#14161C',
      onSurface: '#F8F8F8',
      surfaceVariant: '#2B2B2B',
      onSurfaceVariant: '#808080'
    },
    light: {
      primary: '#151E74',
      onPrimary: '#FFFFFF',
      secondary: '#FFDE33',
      onSecondary: '#1A1A1A',
      error: '#BA3B1B',
      secondaryContainer: '#F5F5F5',
      onSecondaryContainer: '#1A1A1A',
      surface: '#FFFFFF',
      onSurface: '#1A1A1A',
      surfaceVariant: '#F5F5F5',
      onSurfaceVariant: '#808080'
    }
  },
  elements: {
    button: {
      host: {
        borderRadius: '8px',
        fontSize: '14px',
        fontWeight: '500',
        padding: '14px 16px'
      },
      size: {
        small: {
          host: {
            fontSize: '12px',
            padding: '6px 8px'
          }
        }
      },
      variant: {
        text: {
          host: {
            borderBottom: '1px solid',
            hover: {
              opacity: '0.1'
            }
          }
        }
      }
    },
    camera: {
      host: {
        after: {
          background: 'linear-gradient(0deg, rgba(0, 0, 0, 0.25) 0%, rgba(0, 0, 0, 0) 100%)',
          height: '25%'
        },
        before: {
          background: 'linear-gradient(180deg, rgba(0, 0, 0, 0.25) 0%, rgba(0, 0, 0, 0) 100%)',
          height: '25%'
        }
      }
    },
    cameraCorners: {
      svg: {
        strokeWidth: '3px'
      }
    },
    cameraInstructions: {
      host: {
        gap: '8px'
      },
      li: {
        borderRadius: '8px',
        gap: '16px',
        padding: '16px'
      },
      liText: {
        fontSize: '14px'
      }
    },
    cameraSelect: {
      labels: {
        gap: '12px',
        padding: '24px 0 calc(24px - 8px) 0'
      },
      labelsHeadline: {
        fontSize: '24px',
        fontWeight: '500'
      },
      labelsText: {
        fontSize: '16px',
        fontWeight: '500'
      },
      list: {
        borderRadius: '16px',
        margin: '16px',
        padding: '16px',
        top: '32px !important'
      },
      option: {
        borderRadius: '8px',
        marginTop: '8px',
        padding: '20px 16px'
      }
    },
    dialog: {
      host: {
        border: '1px solid',
        borderRadius: '16px',
        boxShadow: '0px 4px 16px rgba(0, 0, 0, 0.25)'
      }
    },
    poweredBy: {
      host: {
        gap: '6px',
        height: '12px'
      },
      icon: {
        height: '7px',
        width: '55px'
      },
      span: {
        fontSize: '9px',
        fontWeight: '500',
        letterSpacing: '2px'
      }
    },
    qrcode: {
      host: {
        borderRadius: '8px',
        padding: '4px'
      }
    },
    root: {
      buttonCameraSelect: {
        right: 'calc(16px + 18px + 16px)',
        top: '18px'
      },
      buttonCancel: {
        left: '16px',
        top: '16px'
      },
      buttonClose: {
        left: '16px',
        top: '16px'
      },
      buttonFlash: {
        right: '18px',
        top: '18px'
      },
      buttonPin: {
        height: '16px',
        width: '16px'
      },
      buttonsSwitchToMobileChoice: {
        gap: '4px'
      },
      cameraBiometric: {
        width: '100%'
      },
      cameraTip: {
        backdropFilter: 'blur(4px)',
        borderRadius: '4px',
        fontSize: '12px',
        fontWeight: '500',
        height: '24px',
        padding: '0px 8px',
        top: '14px'
      },
      headline: {
        fontSize: '24px',
        fontWeight: '600',
        marginTop: '32px'
      },
      host: {
        borderRadius: '16px',
        gap: '32px',
        padding: '16px 16px 28px 16px'
      },
      poweredBy: {
        bottom: '8px'
      },
      text: {
        fontSize: '16px',
        fontWeight: '400'
      },
      texts: {
        gap: '8px'
      }
    }
  }
}

Example: Setting colors

The following example shows how to change the primary and secondary colors by updating the themeOptions property of the PingOne Recognize authorization object (auth):

Details
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Auth</title>
    <style>
      * {
        box-sizing: border-box;
      }

      body {
        align-items: center;
        display: flex;
        justify-content: center;
        margin: 0;
        min-height: 100vh;
        padding: 8px;
      }

      kl-auth {
        border: 1px solid lightgray;
      }
    </style>
  </head>
  <body>
    <kl-auth
      customer="CUSTOMER_NAME"
      enable-camera-instructions
      key="IMAGE_ENCRYPTION_PUBLIC_KEY"
      key-id="IMAGE_ENCRYPTION_KEY_ID"
      lang="en"
      size="375"
      theme="light"
      transaction-data='DATA_FROM_CUSTOMER_SERVER_TO_BE_SIGNED'
      username="USERNAME"
      ws-url="KEYLESS_AUTHENTICATION_SERVICE_URL"
    ></kl-auth>
    <script src="./node_modules/@keyless/sdk-web-components/index.js" type="module"></script>
    <script>
      const auth = document.querySelector('kl-auth')

      auth.themeOptions = {
        colors: {
          light: {
            primary: '#000',
            onPrimary: '#fff',
            surface: '#fff',
            onSurface: '#000'
          }
        }
      }
    </script>
  </body>
</html>

Slots

The PingOne Recognize Web SDK uses <slot> elements to customize the following components:

  • <kl-auth>

  • <kl-auth-dialog>

  • <kl-enroll>

  • <kl-enroll-dialog>

Each component supports the following slots:

button-cancel

The circle button with the left arrow icon on the top left of the component. When clicked, it reconnects to the WebSocket and restarts the process from the beginning.

button-close

The circle button with the X icon on the top right of the component. when clicked, it closes the WebSocket connection and activates the close event used by the dialog components to close the dialog.

lottie-spinner

The spinner animation.

lottie-done

The checkmark animation.

lottie-error

The error animation.

texts

The title and description block.

camera-instructions

The list of camera instructions that the user should follow to perform a successful authentication or enrollment.

button-camera-permissions

The button that checks and potentially requests the camera permission to the user.

camera-tip

The text tip on the top of the component when the cameras are on that suggests to the user how to better frame themself, only shown if camera checks are enabled.

camera-select

The select that is shown when the user is on a desktop device and has multiple cameras, grants the user the capability of picking a different camera than the default one.

button-flash

The circle button with the flash icon on the top right of the component, its action is to force a white background on the screen.

camera-backdrop

The blurry camera stream that stays behind the main camera stream, its purpose is mainly for design.

camera-biometric

The main camera stream, useful for the user to adjust their camera quality in realtime.

buttons-stm-choice

The switch to mobile buttons, the primary button leads to a qrcode that lets the user continue the flow on their phone. The secondary button lets the user continue the flow on their current device.

stm-qrcode

The QR code that allows the user to continue the flow on their phone.

button-retry

The retry button, reconnects to the WebSocket and restarts the whole flow from zero.

powered-by

The powered by PingOne Recognize element, always visible when enabled at the bottom of the component.

Example: Changing the spinner

This example changes the appearance of the spinner in the authorization component. It defines a custom CSS style rule (#custom-spinner) and then assigns it to the spinner slot of the <kl-auth> element.

Details
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Auth</title>
    <style>
      * {
        box-sizing: border-box;
      }

      body {
        align-items: center;
        display: flex;
        justify-content: center;
        margin: 0;
        min-height: 100vh;
        padding: 8px;
      }

      kl-auth {
        border: 1px solid lightgray;
      }

      #custom-spinner {
        width: 48px;
        height: 48px;
        border: 5px solid black;
        border-bottom-color: transparent;
        border-radius: 50%;
        display: inline-block;
        box-sizing: border-box;
        animation: rotation 1s linear infinite;
      }

      @keyframes rotation {
        0% {
            transform: rotate(0deg);
        }
        100% {
            transform: rotate(360deg);
        }
      }
    </style>
  </head>
  <body>
    <kl-auth
      customer="CUSTOMER_NAME"
      enable-camera-instructions
      key="IMAGE_ENCRYPTION_PUBLIC_KEY"
      key-id="IMAGE_ENCRYPTION_KEY_ID"
      lang="en"
      size="375"
      theme="light"
      transaction-data='DATA_FROM_CUSTOMER_SERVER_TO_BE_SIGNED'
      username="USERNAME"
      ws-url="KEYLESS_AUTHENTICATION_SERVICE_URL"
    >
      <div id="custom-spinner" slot="spinner"></div>
    </kl-auth>
    <script src="./node_modules/@keyless/sdk-web-components/index.js" type="module"></script>
  </body>
</html>

Example: Retry when liveness fails

This example shows how to display components in specific situations. Here:

  • The Retry button is initially hidden by assigning a custom CSS rule to a custom button named custom-button-retry.

  • The button is assigned to the button-retry slot of the <kl-auth> elements.

  • Events are added to the button to control visibility when the button is clicked and when the liveness check fails.