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:
| Component | Styling class | What it controls |
|---|---|---|
| Card form | CardFormStylesConfig | Wrapper, input fields, labels, errors, checkbox |
| Pay button | CardButtonStyle | Background, 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:
| Property | Default |
|---|---|
| Field border color | Color.Black |
| Field border width | 1.dp |
| Field corner radius | 2.dp |
| Field text color | Color.Black |
| Focus border color | #D63D00 (Payrails orange) |
| Focus cursor color | #D63D00 |
| Completed border color | Color.Green |
| Invalid border color | Color.Red |
| Error text color | Color.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:
| Property | Type | Description |
|---|---|---|
textColor | Color? | Text color |
backgroundColor | Color? | Background fill |
borderColor | Color? | Border color |
borderWidth | Dp? | Border width |
cornerRadius | Dp? | Corner radius |
font | TextStyle? | Font/text style (size, weight, family) |
padding | PaddingValues? | Content padding |
width | Dp? | Fixed width |
height | Dp? | Fixed height |
minWidth | Dp? | Minimum width |
maxWidth | Dp? | Maximum width |
minHeight | Dp? | Minimum height |
maxHeight | Dp? | Maximum height |
placeholderColor | Color? | Placeholder text color |
cursorColor | Color? | 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
| Property | Type | Default | Description |
|---|---|---|---|
backgroundColor | Color? | #1976D2 (blue) | Button background |
textColor | Color? | Color.White | Button label color |
font | TextStyle? | System default | Label text style |
cornerRadius | Dp? | 8.dp | Corner radius |
borderWidth | Dp? | None | Border width |
borderColor | Color? | None | Border color |
contentPadding | PaddingValues? | Material default | Content padding |
height | Dp? | Wrap content | Fixed height |
fillMaxWidth | Boolean | true | Stretch to fill width |
disabledStyle | CardButtonStyle? | Gray (#E0E0E0) | Override for disabled state |
loadingStyle | CardButtonStyle? | None | Override for loading state |
loadingIndicatorColor | Color? | Color.White | Spinner 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_DATEis a combined MM/YY field (use withshowSingleExpiryDateField = true)EXPIRATION_MONTH+EXPIRATION_YEARare separate fields (use withoutshowSingleExpiryDateField)- Cannot mix
EXPIRATION_DATEwithEXPIRATION_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"
))
)
)
)Updated 1 day ago