Docs navigation

className overrides

Pass classes through the classNames prop to style individual elements. Use this when appearance.variables is too broad — a wider modal, a two-column reason grid, a different button color on one specific step.

Any class-based styling works: Tailwind, CSS modules, plain global classes, CSS-in-JS that emits classes. The SDK appends your class to the element's built-in classes and your CSS engine handles specificity.

Structural classNames

Top-level classNames styles the modal chrome — the dialog surface, the overlay, the close and back buttons.

<CancelFlow
  classNames={{
    modal: 'max-w-lg shadow-2xl',
    overlay: 'bg-black/60 backdrop-blur-sm',
    closeButton: 'top-4 right-4',
    backButton: 'text-gray-400 hover:text-gray-600',
  }}
  steps={steps}
  handleCancel={handleCancel}
/>
KeyElement
modalThe dialog surface.
overlayThe dimmed backdrop behind the modal.
closeButtonThe X in the corner.
backButtonThe back arrow shown on non-first steps.

Per-step classNames

Per-step classNames live on the step config rather than at the top level. Each step type has its own className shape:

{
  type: 'survey',
  classNames: {
    root: 'space-y-3',
    title: 'text-3xl font-semibold',
    reasonList: 'grid grid-cols-2 gap-2',
    reasonButton: 'border-gray-200 hover:border-gray-300',
    continueButton: 'bg-purple-600 hover:bg-purple-700',
  },
  reasons: [/* ... */],
}

The full shapes:

interface SurveyClassNames {
  root?:                 string
  title?:                string
  description?:          string
  reasonList?:           string
  reasonButton?:         string
  reasonButtonSelected?: string
  reasonLabel?:          string
  followupInput?:        string
  continueButton?:       string
}
 
interface OfferClassNames {
  root?:             string
  title?:            string
  description?:      string
  card?:             string
  headline?:         string
  body?:             string
  acceptButton?:     string
  declineButton?:    string
  pauseSlider?:      string   // pause offer only — applied to the months chip row
}
 
interface FeedbackClassNames {
  root?:           string
  title?:          string
  description?:    string
  textarea?:       string
  characterCount?: string
  submitButton?:   string
}
 
interface ConfirmClassNames {
  root?:            string
  title?:           string
  description?:     string
  lossList?:        string
  lossLabel?:       string
  lossItem?:        string
  lossBullet?:      string
  confirmButton?:   string
  goBackButton?:    string
  periodEndNotice?: string
}
 
interface SuccessClassNames {
  root?:        string
  icon?:        string
  title?:       string
  description?: string
  closeButton?: string
}

OfferClassNames is shared across every offer type. pauseSlider applies only on pause offers (it targets the months chip row); the rest apply on every offer type. To restyle the plan picker, discount card, or trial-extension badge beyond what card / acceptButton reach, override the per-offer-type component — see Replacing components.

How overrides merge

The SDK emits its own class names on every element (.ck-reason-button, .ck-feedback-field, etc., with modifier suffixes like --selected or --focused). Your class is appended:

classNames={{ reasonButton: 'border-purple-300' }}
// Renders as: class="ck-reason-button border-purple-300"

Specificity follows normal CSS rules. Tailwind utilities are usually authored later in the cascade, so they win automatically. With CSS modules or plain CSS, your class and the SDK's .ck-* class sit at the same specificity — you may need !important or a more specific selector to override.

Patterns

Wider modal with a tighter shadow:

classNames={{
  modal: 'max-w-md rounded-2xl shadow-[0_24px_60px_rgba(0,0,0,0.18)]',
}}

Two-column reason grid:

steps={[{
  type: 'survey',
  classNames: { reasonList: 'grid grid-cols-2 gap-2' },
  reasons: [/* ... */],
}]}

Brand-colored continue button on one step:

steps={[{
  type: 'survey',
  classNames: { continueButton: 'bg-emerald-600 hover:bg-emerald-700 text-white' },
  reasons: [/* ... */],
}]}

For a brand color that applies across every step, set colorPrimary in appearance.variables instead — see Theming.

When classNames isn't enough

Reach for component overrides when you need to:

  • Restructure the markup — rendering reasons as cards with images, for example.
  • Add elements the SDK doesn't emit — a quote alongside the discount offer, an avatar next to each reason.
  • Replace an entire step's UI while keeping the state machine.

classNames don't require maintaining a React component as the SDK evolves, but they can only style what the SDK already renders. Component overrides give you total control at the cost of keeping the component in sync.

Next steps