SDK: Styling
The widget works without any CSS. To match it to your site, pick the level you need:
- Theme: light, dark, or auto. One
init()option, no CSS. - Catalog styles: a ready-made look copied from the style catalog, then changed to fit your brand.
- 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:
| Value | Result |
|---|---|
'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.
-
Open the catalog and pick a style. Its page shows it on every survey type.
-
Click Copy above the style's CSS.
-
Paste the CSS into your site's stylesheet, outside any
@layer. On a site builder, use its custom CSS or custom code setting. -
Pass the style's name as
classNameand its theme asthemetoinit(). The comment at the top of the copied CSS names both:ftools('init', { surveyId: '__SURVEY_ID__', publishableKey: '__PUBLISHABLE_KEY__', className: 'terminal', theme: 'dark' }); -
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-indexand the rest of the CSS variables. Variables reach the widget from any ancestor,:rootincluded. ::part()styles one part:submit,field,choice,titleand 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-popupand.ftools-embednarrow 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,
autoincluded: the built-in theme doesn't adjust it. See How styles combine. - The popup's default z-index is 49. Raise
--ft-popup-z-indexif 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-embedor.ftools-popupto 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.