Jump to

SDK: Styling

The widget works without any CSS. To match it to your site, pick the level you need:

  1. Theme: light, dark, or auto. One init() option, no CSS.
  2. Catalog styles: a ready-made look copied from the style catalog, then changed to fit your brand.
  3. Custom styles: your own CSS for colors, radius, shadows, font, sizes, and every part of the widget.

Theme (dark mode)

The widget is light by default. theme in init() changes it, in both modes:

Comparison table
ValueResult
'light'Default light widget. Same as leaving theme out.
'dark'Dark widget, for a dark site.
'auto'Follows the visitor's system color scheme and switches when it changes.
ftools('init', {
  surveyId: '__SURVEY_ID__',
  publishableKey: '__PUBLISHABLE_KEY__',
  theme: 'dark'
});

The SDK reads the theme once, at init(). If your site has its own light/dark switch, pass its current value, or use 'auto' when the site follows the system scheme.

Custom styles

Your CSS can change colors, radius, shadows, font, sizes and every part of the widget. The markup, the step flow and the icons stay the same.

Use a style from the catalog

The fastest start is the style catalog: ready-made styles from minimal to a hand-drawn sketch.

  1. Open the catalog and pick a style. Its page shows it on every survey type.

  2. Click Copy above the style's CSS.

  3. Paste the CSS into your site's stylesheet, outside any @layer. On a site builder, use its custom CSS or custom code setting.

  4. Pass the style's name as className and its theme as theme to init(). The comment at the top of the copied CSS names both:

    ftools('init', {
      surveyId: '__SURVEY_ID__',
      publishableKey: '__PUBLISHABLE_KEY__',
      className: 'terminal',
      theme: 'dark'
    });
  5. Change the CSS to fit your brand. Start with the --ft-* variables at the top: colors, radius, font.

If your site builder's integration doesn't let you set className, delete the style's name from the copied selectors: replace .terminal with nothing, so .ftools-survey.terminal::part(submit) becomes .ftools-survey::part(submit). The style then applies to every survey on the page. If you can't set theme either, the widget stays light, so pick a style whose comment lists 'light'.

Catalog styles are examples to copy, not a package: they aren't versioned or updated with the SDK, and the CSS you paste is yours to change. The same CSS also works with react-feedback-surveys in a React app.

Variables and parts

The widget renders inside a shadow root, so your site's CSS can't break it, and your classes can't reach inside it. Style it through its host element, .ftools-survey:

  • CSS variables set colors (any CSS color), radius, shadows, font and z-index: --ft-color-bg, --ft-surface-radius, --ft-popup-z-index and the rest of the CSS variables. Variables reach the widget from any ancestor, :root included.
  • ::part() styles one part: submit, field, choice, title and the rest of the parts. States come as a second name: ::part(choice checked).
.ftools-survey {
  --ft-color-bg: #f7f5ff;
  --ft-surface-radius: 18px;
  --ft-popup-z-index: 1000;
}

.ftools-survey::part(submit) {
  color: #fff;
  background-color: #6d4aff;
  border-radius: 999px;
}

.ftools-survey::part(choice checked) {
  background-color: #ede9fe;
}
  • .ftools-popup and .ftools-embed narrow a rule to one mode.
  • Your rules always win over the widget's own styles. A rule that sets a color itself keeps that color in every theme, auto included: the built-in theme doesn't adjust it. See How styles combine.
  • The popup's default z-index is 49. Raise --ft-popup-z-index if your header or chat widget covers it.

Style one widget

To style one widget when several are on the page, pass className to init(). The SDK adds it to that widget's host, next to .ftools-survey:

ftools('init', {
  surveyId: '__SURVEY_ID_A__',
  publishableKey: '__PUBLISHABLE_KEY__',
  className: 'checkout-survey'
});

ftools('init', {
  surveyId: '__SURVEY_ID_B__',
  publishableKey: '__PUBLISHABLE_KEY__',
  className: 'docs-survey',
  root: '#feedback'
});
.ftools-survey.checkout-survey {
  --ft-color-accent: #6d4aff;
}

.ftools-survey.docs-survey::part(submit) {
  border-radius: 0;
}

Feedback button

The floating feedback button has its own host, .ftools-feedback-button, with two parts: button and logo. className doesn't reach it. It takes the theme of the survey it opens and reads the same --ft-* variables:

.ftools-feedback-button {
  --ft-color-bg: #f7f5ff;
}

.ftools-feedback-button::part(button) {
  border-radius: 16px 0 0 16px;
}

Rules for writing a style

These are the rules the catalog styles follow. Use them to write a style from scratch, or to ask an AI assistant to write one:

  • Scope every rule to the host and a name of your own: .ftools-survey.my-style. Add .ftools-embed or .ftools-popup to target one mode only.
  • Set colors, radius, shadows, and font with --ft-* variables first. Use ::part() for what variables don't cover.
  • A state is a second part name: ::part(choice checked), ::part(checkbox checked), ::part(field invalid), ::part(point-icon filled), ::part(root success).
  • Pseudo-classes and pseudo-elements of the part itself work: ::part(submit):hover, ::part(field)::placeholder, ::part(surface)::before.
  • ::part() can't look inside or around a part: no ::part(a) .child, no :has(), no ::part(a) + ::part(b).

The full lists: CSS variables, part names, and states.