# 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 ```kotlin 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: ```kotlin 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 ```kotlin 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: ```kotlin 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: ```kotlin 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: ```kotlin 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: ```kotlin 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: ```kotlin 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" )) ) ) ) ```