About customisation
Learn about how you can customise card components.
Overview
Card components come with responsive and accessible default styling, but are designed to be fully customisable.
Core styling architecture
State-based styling system
All styling configurations support multiple states to provide dynamic visual feedback:
interface StateStyles {
base?: CSSProperties; // Default appearance
valid?: CSSProperties; // When field passes validation
invalid?: CSSProperties; // When field fails validation
}
interface ComponentStyles {
base?: CSSProperties; // Container styles
valid?: CSSProperties; // Valid state container
invalid?: CSSProperties; // Invalid state container
input?: CSSProperties; // Input element specific styles
}
CSS properties integration
All styling options use React's CSSProperties
interface, providing:
- Type safety: IntelliSense support and compile-time validation.
- Comprehensive properties: Access to all CSS properties.
- Pseudo-selectors: Support for
:hover
,:focus
,:active
states. - Camel case conversion: Automatic conversion from camelCase to kebab-case.
Component-specific styling options
Field components (input-based)
All field components inherit from FieldComponentConfig
and support comprehensive styling:
interface FieldComponentConfig {
// Container styling with state support
componentStyles?: ComponentStyles;
// Input element styling with states
inputStyles?: StateStyles;
// Label styling with states
labelStyles?: StateStyles;
// Label positioning
labelPosition?: 'above' | 'below' | 'left' | 'right';
isFloatingLabel?: boolean;
// Error message styling
invalidTextStyles?: CSSProperties;
invalidTextPosition?: 'above' | 'below' | 'left' | 'right';
// Icon customisation
validIconSrc?: string;
invalidIconSrc?: string;
}
Example
Here's an example of a customised card number component.
const cardNumberConfig: CardNumberComponentConfig = {
// Container styling
componentStyles: {
base: {
backgroundColor: '#f8f9fa',
borderRadius: '8px',
padding: '16px',
boxShadow: '0 2px 4px rgba(0,0,0,0.1)'
},
valid: {
borderColor: '#28a745',
backgroundColor: '#f8fff8'
},
invalid: {
borderColor: '#dc3545',
backgroundColor: '#fff8f8'
}
},
// Input field styling
inputStyles: {
base: {
fontSize: '18px',
fontWeight: '500',
color: '#2c3e50',
border: '2px solid #e9ecef',
borderRadius: '6px',
padding: '12px 16px',
transition: 'all 0.3s ease'
},
valid: {
borderColor: '#28a745',
':focus': {
borderColor: '#20c997',
boxShadow: '0 0 0 3px rgba(40, 167, 69, 0.1)'
}
},
invalid: {
borderColor: '#dc3545',
':focus': {
borderColor: '#c82333',
boxShadow: '0 0 0 3px rgba(220, 53, 69, 0.1)'
}
}
},
// Label styling
labelStyles: {
base: {
fontSize: '14px',
fontWeight: '600',
color: '#495057',
marginBottom: '8px',
textTransform: 'uppercase',
letterSpacing: '0.5px'
},
invalid: {
color: '#dc3545'
}
},
// Floating label configuration
isFloatingLabel: true,
labelPosition: 'above',
// Error message styling
invalidTextStyles: {
fontSize: '12px',
color: '#dc3545',
fontStyle: 'italic',
marginTop: '4px'
},
// Icon customisation
showHintIcon: true,
hintIconSrc: 'https://example.com/card-icon.svg'
};
Card submit
const submitButtonConfig: CardSubmitComponentConfig = {
submitText: 'Complete payment',
styles: {
base: {
backgroundColor: '#6c5ce7',
color: '#ffffff',
fontSize: '16px',
fontWeight: '600',
padding: '14px 32px',
borderRadius: '8px',
border: 'none',
cursor: 'pointer',
transition: 'all 0.2s ease',
textTransform: 'uppercase',
letterSpacing: '0.5px'
},
hover: {
backgroundColor: '#5a52d5',
transform: 'translateY(-1px)',
boxShadow: '0 4px 12px rgba(108, 92, 231, 0.3)'
},
disabled: {
backgroundColor: '#95a5a6',
cursor: 'not-allowed',
transform: 'none',
boxShadow: 'none'
}
}
}
};
Country selection
const countryConfig: CountrySelectionComponentConfig = {
// Base field styling
componentStyles: {
base: {
position: 'relative',
width: '100%'
}
},
// Dropdown container styling
dropdownStyles: {
backgroundColor: '#ffffff',
border: '1px solid #dee2e6',
borderRadius: '0 0 12px 12px',
boxShadow: '0 8px 25px rgba(0, 0, 0, 0.15)',
maxHeight: '250px',
overflowY: 'auto',
zIndex: 1000
},
// Dropdown option styling
dropdownOptionStyles: {
unselected: {
padding: '12px 16px',
cursor: 'pointer',
borderBottom: '1px solid #f8f9fa',
transition: 'background-color 0.2s ease'
},
hover: {
backgroundColor: '#e3f2fd',
borderLeft: '3px solid #2196f3'
},
selected: {
backgroundColor: '#e8f5e8',
color: '#2e7d32',
fontWeight: '600',
borderLeft: '3px solid #4caf50'
}
}
};
Checkbox components
const checkboxConfig: CheckboxComponentConfig = {
label: 'Save payment method for future use',
checkedColor: '#4caf50',
labelStyles: {
checked: {
color: '#2e7d32',
fontWeight: '500'
},
unchecked: {
color: '#6c757d',
fontWeight: '400'
}
}
};
Advanced styling patterns
Pseudo-selectors support
const advancedInputStyles = {
base: {
fontSize: '16px',
padding: '12px',
// Pseudo-selectors using key prefixes
':hover': {
borderColor: '#007bff'
},
':focus': {
borderColor: '#0056b3',
boxShadow: '0 0 0 3px rgba(0, 123, 255, 0.1)'
},
':active': {
transform: 'translateY(1px)'
},
'::placeholder': {
color: '#6c757d',
fontStyle: 'italic'
}
}
};
Responsive design
const responsiveStyles = {
base: {
fontSize: '16px',
padding: '12px',
// Use CSS custom properties for responsive behavior
'--mobile-padding': '8px',
'--desktop-padding': '16px',
// Media queries via CSS-in-JS (if supported)
'@media (max-width: 768px)': {
fontSize: '14px',
padding: 'var(--mobile-padding)'
},
'@media (min-width: 769px)': {
padding: 'var(--desktop-padding)'
}
}
};
Typography and branding
Consistent typography system
const typographyTheme = {
// Font families
primaryFont: 'Inter, system-ui, -apple-system, sans-serif',
monospaceFont: 'SF Mono, Monaco, monospace',
// Font sizes
fontSizes: {
xs: '12px',
sm: '14px',
base: '16px',
lg: '18px',
xl: '20px'
},
// Line heights
lineHeights: {
tight: '1.2',
normal: '1.5',
relaxed: '1.7'
}
};
// Apply to components
const cardNumberConfig = {
inputStyles: {
base: {
fontFamily: typographyTheme.primaryFont,
fontSize: typographyTheme.fontSizes.base,
lineHeight: typographyTheme.lineHeights.normal
}
},
labelStyles: {
base: {
fontFamily: typographyTheme.primaryFont,
fontSize: typographyTheme.fontSizes.sm,
lineHeight: typographyTheme.lineHeights.tight
}
}
};
Brand colour system
const brandColors = {
primary: '#6c5ce7',
secondary: '#00b894',
accent: '#fd79a8',
neutral: {
50: '#f8f9fa',
100: '#e9ecef',
200: '#dee2e6',
500: '#6c757d',
900: '#212529'
},
semantic: {
success: '#00b894',
warning: '#fdcb6e',
error: '#e17055',
info: '#74b9ff'
}
};
// Apply brand colours
const brandedComponentStyles = {
base: {
backgroundColor: brandColors.neutral[50],
borderColor: brandColors.neutral[200]
},
valid: {
borderColor: brandColors.semantic.success,
color: brandColors.semantic.success
},
invalid: {
borderColor: brandColors.semantic.error,
color: brandColors.semantic.error
}
};
Animation and transitions
const animatedStyles = {
base: {
transition: 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)',
transform: 'translateY(0)',
opacity: 1
},
invalid: {
transform: 'translateX(2px)',
animation: 'shake 0.5s ease-in-out'
}
};
// Add keyframes via CSS injection
const keyframes = `
@keyframes shake {
0%, 100% { transform: translateX(0); }
25% { transform: translateX(-2px); }
75% { transform: translateX(2px); }
}
`;
Updated 3 days ago