Styling Guide

The PaySight Widget provides extensive styling and theming options to match your application’s design. This guide covers all available styling options and best practices.

Theme Configuration

The widget’s appearance can be customized through the theme configuration option:

const config = {
  // ... other config options
  theme: {
    // Google Font link (optional)
    font: 'https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap',
    
    // Custom CSS styles
    css: {
      // Your custom styles
    },
    
    // Pre-defined theme name (optional)
    themeName: 'light' // or 'dark'
  }
};

Basic Styling

Root Variables

Define global variables for consistent styling:

const config = {
  theme: {
    css: {
      ":root": {
        "--primary-color": "#3b82f6",
        "--error-color": "#ef4444",
        "--text-color": "#0f172a",
        "--border-color": "#e5e7eb",
        "--background-color": "#ffffff",
        "--input-height": "40px",
        "--border-radius": "6px"
      }
    }
  }
};

Container Styles

Style the widget container:

const config = {
  theme: {
    css: {
      ".widget-container": {
        padding: "20px",
        backgroundColor: "var(--background-color)",
        borderRadius: "8px",
        boxShadow: "0 1px 3px rgba(0, 0, 0, 0.1)"
      }
    }
  }
};

Input Styles

Customize form input fields:

const config = {
  theme: {
    css: {
      input: {
        height: "var(--input-height)",
        padding: "0 12px",
        fontSize: "16px",
        borderRadius: "var(--border-radius)",
        border: "1px solid var(--border-color)",
        color: "var(--text-color)",
        transition: "border-color 0.2s ease",
        
        "&::placeholder": {
          color: "#94a3b8"
        },
        
        "&:focus": {
          outline: "none",
          borderColor: "var(--primary-color)",
          boxShadow: "0 0 0 3px rgba(59, 130, 246, 0.1)"
        }
      }
    }
  }
};

Button Styles

Style the submit button:

const config = {
  theme: {
    css: {
      button: {
        backgroundColor: "var(--primary-color)",
        color: "#ffffff",
        padding: "0 16px",
        height: "var(--input-height)",
        borderRadius: "var(--border-radius)",
        fontSize: "16px",
        border: "none",
        cursor: "pointer",
        transition: "background-color 0.2s ease",
        
        "&:hover": {
          backgroundColor: "#2563eb"
        },
        
        "&:focus": {
          outline: "none",
          boxShadow: "0 0 0 3px rgba(59, 130, 246, 0.1)"
        }
      }
    }
  }
};

Advanced Styling

Component-Specific Styles

Style specific widget components:

const config = {
  theme: {
    css: {
      // Card input field
      "[ev-component=card]": {
        gap: "16px",
        
        // Card number field
        ".field[ev-name=number]": {
          position: "relative",
          
          "& input": {
            paddingLeft: "48px" // Space for card icon
          },
          
          "& .icon": {
            position: "absolute",
            left: "12px",
            top: "50%",
            transform: "translateY(-50%)",
            width: "24px",
            height: "24px"
          }
        },
        
        // Expiry and CVC fields
        ".field[ev-name=expiry], .field[ev-name=cvc]": {
          "& input": {
            textAlign: "center"
          }
        }
      },
      
      // Address fields
      "[ev-component=address]": {
        display: "grid",
        gridTemplateColumns: "1fr 1fr",
        gap: "16px",
        
        ".field[ev-name=street]": {
          gridColumn: "1 / -1"
        }
      }
    }
  }
};

State-Based Styling

Style different field states:

const config = {
  theme: {
    css: {
      // Focus state
      "input:focus": {
        outline: "none",
        borderColor: "var(--primary-color)",
        boxShadow: "0 0 0 3px rgba(59, 130, 246, 0.1)"
      },
      
      // Error state
      ".field[ev-valid=false]": {
        "& input": {
          borderColor: "var(--error-color)",
          color: "var(--error-color)"
        },
        
        "& .error": {
          color: "var(--error-color)",
          fontSize: "12px",
          marginTop: "4px"
        }
      },
      
      // Disabled state
      "input:disabled": {
        backgroundColor: "#f8fafc",
        cursor: "not-allowed",
        opacity: 0.7
      },
      
      // Loading state
      ".loading": {
        opacity: 0.7,
        pointerEvents: "none"
      }
    }
  }
};

Responsive Design

Implement responsive styles:

const config = {
  theme: {
    css: {
      // Base styles
      ".widget-container": {
        width: "100%",
        maxWidth: "500px",
        margin: "0 auto",
        padding: "20px"
      },
      
      // Mobile styles
      "@media (max-width: 640px)": {
        ".widget-container": {
          padding: "16px"
        },
        input: {
          fontSize: "14px"
        },
        button: {
          width: "100%"
        }
      }
    }
  }
};

Pre-defined Themes

Use built-in themes as a starting point:

// Light theme (default)
const config = {
  theme: {
    themeName: 'light'
  }
};

// Dark theme
const config = {
  theme: {
    themeName: 'dark'
  }
};

Dynamic Theme Updates

Update the widget’s theme after initialization:

// Update theme
widget.update({
  theme: {
    css: {
      ":root": {
        "--primary-color": "#0ea5e9"
      }
    }
  }
});

// Switch to dark theme
widget.update({
  theme: {
    themeName: 'dark'
  }
});

Best Practices

1. Use CSS Variables

Define and use CSS variables for consistent styling:

const config = {
  theme: {
    css: {
      ":root": {
        "--primary": "#3b82f6",
        "--primary-dark": "#2563eb",
        "--error": "#ef4444",
        "--success": "#22c55e"
      },
      button: {
        backgroundColor: "var(--primary)",
        "&:hover": {
          backgroundColor: "var(--primary-dark)"
        }
      }
    }
  }
};

2. Maintain Consistency

Create a theme object for consistent values:

const theme = {
  colors: {
    primary: "#3b82f6",
    error: "#ef4444",
    text: "#0f172a",
    border: "#e5e7eb"
  },
  spacing: {
    sm: "8px",
    md: "16px",
    lg: "24px"
  },
  borderRadius: {
    sm: "4px",
    md: "6px",
    lg: "8px"
  }
};

const config = {
  theme: {
    css: {
      input: {
        padding: theme.spacing.md,
        borderRadius: theme.borderRadius.md,
        borderColor: theme.colors.border
      }
    }
  }
};

3. Follow Accessibility Guidelines

Ensure your styles maintain accessibility:

const config = {
  theme: {
    css: {
      // Ensure sufficient color contrast
      button: {
        backgroundColor: "#2563eb",
        color: "#ffffff",
        
        // Visible focus indicators
        "&:focus": {
          outline: "2px solid #60a5fa",
          outlineOffset: "2px"
        }
      },
      
      // Clear error states
      ".error": {
        color: "#ef4444",
        fontWeight: "500"
      }
    }
  }
};

Next Steps