Styling Guide

This guide shows how to customize the appearance of Payrails SDK components to match your brand.

Overview

The SDK has two main styling surfaces:

ComponentStyling classWhat it controls
Card formCardFormStylesConfigWrapper, input fields, labels, errors, checkbox
Pay buttonCardButtonStyleBackground, text, border, corner radius, loading/disabled states

Styles are set at creation time and use Compose-native types (Color, Dp, TextStyle, PaddingValues).

Card Form Styling

Style Hierarchy

Card form styles cascade from general to specific:

CardFormStylesConfig
├── wrapperStyle          → outer container (background, border, padding)
├── baseStyle             → applies to all input fields as a baseline
├── allInputFieldStyles   → field states (base, focus, completed, invalid)
├── inputFieldStyles      → per-field overrides (keyed by CardFieldType)
├── labelStyles           → per-field label overrides
├── errorTextStyle        → error message appearance
├── storeInstrumentCheckboxStyle      → checkbox wrapper
└── storeInstrumentCheckboxLabelStyle → checkbox label text

Basic Example

val cardForm = Payrails.createCardForm(
    config = CardFormConfig(
        showCardHolderName = true,
        showSingleExpiryDateField = true,
        styles = CardFormStylesConfig(
            wrapperStyle = CardWrapperStyle(
                backgroundColor = Color(0xFFF5F5F5),
                cornerRadius = 12.dp,
                padding = PaddingValues(16.dp)
            ),
            allInputFieldStyles = CardFieldSpecificStyles(
                base = CardStyle(
                    borderColor = Color(0xFFE0E0E0),
                    borderWidth = 1.dp,
                    cornerRadius = 8.dp,
                    textColor = Color(0xFF212121)
                ),
                focus = CardStyle(
                    borderColor = Color(0xFF1976D2),
                    cursorColor = Color(0xFF1976D2)
                ),
                invalid = CardStyle(
                    borderColor = Color(0xFFD32F2F)
                )
            ),
            errorTextStyle = CardStyle(
                textColor = Color(0xFFD32F2F)
            )
        )
    )
)

Per-Field Styling

Override styles for specific fields:

styles = CardFormStylesConfig(
    allInputFieldStyles = CardFieldSpecificStyles(
        base = CardStyle(borderColor = Color.Gray, cornerRadius = 8.dp)
    ),
    inputFieldStyles = mapOf(
        CardFieldType.CARD_NUMBER to CardFieldSpecificStyles(
            base = CardStyle(borderColor = Color.Black, borderWidth = 2.dp)
        )
    ),
    labelStyles = mapOf(
        CardFieldType.CVV to CardStyle(textColor = Color.DarkGray)
    )
)

Default Values

The SDK applies these defaults if you don't provide styles:

PropertyDefault
Field border colorColor.Black
Field border width1.dp
Field corner radius2.dp
Field text colorColor.Black
Focus border color#D63D00 (Payrails orange)
Focus cursor color#D63D00
Completed border colorColor.Green
Invalid border colorColor.Red
Error text colorColor.Red

Use CardFormStylesConfig.defaultConfig as a baseline and override only what you need. Custom styles are merged over defaults — you only need to specify properties you want to change.

CardStyle Properties

CardStyle (alias of Style) supports these properties:

PropertyTypeDescription
textColorColor?Text color
backgroundColorColor?Background fill
borderColorColor?Border color
borderWidthDp?Border width
cornerRadiusDp?Corner radius
fontTextStyle?Font/text style (size, weight, family)
paddingPaddingValues?Content padding
widthDp?Fixed width
heightDp?Fixed height
minWidthDp?Minimum width
maxWidthDp?Maximum width
minHeightDp?Minimum height
maxHeightDp?Maximum height
placeholderColorColor?Placeholder text color
cursorColorColor?Input cursor color

Pay Button Styling

Button styling is configured on CardPaymentButton, not on the card form.

Basic Example

val payButton = Payrails.createCardPaymentButton(
    translations = CardPaymenButtonTranslations(label = "Pay Now"),
    buttonStyle = CardButtonStyle(
        backgroundColor = Color(0xFF1976D2),
        textColor = Color.White,
        cornerRadius = 8.dp,
        height = 48.dp,
        fillMaxWidth = true
    )
)

Button States

The button supports three visual states. Configure each independently:

val payButton = Payrails.createCardPaymentButton(
    translations = CardPaymenButtonTranslations(label = "Pay Now"),
    buttonStyle = CardButtonStyle(
        // Default (enabled) state
        backgroundColor = Color(0xFF1976D2),
        textColor = Color.White,
        cornerRadius = 8.dp,

        // Disabled state (shown when disabledByDefault=true and form is invalid)
        disabledStyle = CardButtonStyle(
            backgroundColor = Color(0xFFE0E0E0),
            textColor = Color(0xFF9E9E9E),
            borderColor = Color(0xFFBDBDBD),
            borderWidth = 1.dp
        ),

        // Loading state (shown during payment processing)
        loadingStyle = CardButtonStyle(
            backgroundColor = Color(0xFF1565C0)
        ),
        loadingIndicatorColor = Color.White
    )
)

State styles are merged over the base style — you only need to specify properties that differ from the enabled state.

CardButtonStyle Properties

PropertyTypeDefaultDescription
backgroundColorColor?#1976D2 (blue)Button background
textColorColor?Color.WhiteButton label color
fontTextStyle?System defaultLabel text style
cornerRadiusDp?8.dpCorner radius
borderWidthDp?NoneBorder width
borderColorColor?NoneBorder color
contentPaddingPaddingValues?Material defaultContent padding
heightDp?Wrap contentFixed height
fillMaxWidthBooleantrueStretch to fill width
disabledStyleCardButtonStyle?Gray (#E0E0E0)Override for disabled state
loadingStyleCardButtonStyle?NoneOverride for loading state
loadingIndicatorColorColor?Color.WhiteSpinner color

Disable-Until-Valid Pattern

To keep the button disabled until the card form is valid:

val payButton = Payrails.createCardPaymentButton(
    translations = CardPaymenButtonTranslations(label = "Pay Now"),
    disabledByDefault = true,
    buttonStyle = CardButtonStyle(
        backgroundColor = Color(0xFF1976D2),
        textColor = Color.White,
        disabledStyle = CardButtonStyle(
            backgroundColor = Color(0xFFE0E0E0),
            textColor = Color(0xFF9E9E9E)
        )
    )
)

The button automatically enables when the card form becomes valid (all fields pass validation).

Card Form Layout

Customize which fields appear and how they're arranged:

val cardForm = Payrails.createCardForm(
    config = CardFormConfig(
        showCardHolderName = true,
        showSingleExpiryDateField = true,
        layout = listOf(
            listOf(CardFieldType.CARDHOLDER_NAME),           // Row 1: full width
            listOf(CardFieldType.CARD_NUMBER),               // Row 2: full width
            listOf(CardFieldType.EXPIRATION_DATE, CardFieldType.CVV)  // Row 3: side by side
        )
    )
)

Layout Rules

  • Each inner list is a row; fields in the same row share space equally
  • EXPIRATION_DATE is a combined MM/YY field (use with showSingleExpiryDateField = true)
  • EXPIRATION_MONTH + EXPIRATION_YEAR are separate fields (use without showSingleExpiryDateField)
  • Cannot mix EXPIRATION_DATE with EXPIRATION_MONTH/EXPIRATION_YEAR
  • No duplicate fields allowed

Card Network Icons

Show detected card network icons in the card number field:

val cardForm = Payrails.createCardForm(
    config = CardFormConfig(
        showCardIcon = true,
        cardIconAlignment = CardIconAlignment.right  // or .left
    )
)

Icons update automatically as the user types and the SDK detects the card network (Visa, Mastercard, Amex, Discover).

Translations

Customize all user-facing text:

val cardForm = Payrails.createCardForm(
    config = CardFormConfig(
        translations = CardTranslations(
            placeholders = CardTranslations.Placeholders(mutableMapOf(
                CardFieldType.CARDHOLDER_NAME to "Name on card",
                CardFieldType.CARD_NUMBER to "1234 5678 9012 3456",
                CardFieldType.EXPIRATION_DATE to "MM/YY",
                CardFieldType.CVV to "123"
            )),
            labels = CardTranslations.Labels(
                values = mutableMapOf(
                    CardFieldType.CARDHOLDER_NAME to "Name",
                    CardFieldType.CARD_NUMBER to "Card number",
                    CardFieldType.EXPIRATION_DATE to "Expiry",
                    CardFieldType.CVV to "Security code"
                ),
                storeInstrument = "Save this card"
            ),
            error = CardTranslations.ErrorMessages(mutableMapOf(
                CardFieldType.CARD_NUMBER to "Invalid card number"
            ))
        )
    )
)

What’s Next

API Reference