> ## Documentation Index
> Fetch the complete documentation index at: https://formbricks.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Custom CSS & Dark Mode Rollout

> Release order, cache purges and entitlements for shipping Custom CSS, dark mode and the SDK updates

## Context

Custom CSS and dark mode touch the server, the survey renderer, the styling editors and five SDK packages. Mobile apps and self-hosted servers upgrade on their own schedule, so mixed versions are normal, not an edge case. This page is the release checklist; the customer-facing behavior lives in [Custom CSS](/docs/surveys/general-features/custom-css) and [SDK Compatibility](/docs/surveys/website-app-surveys/sdk-compatibility).

## What Ships Where

| Component | Delivered by | Reaches users |
| - | - | - |
| Database columns `Workspace.customCss`, `Workspace.customCssPrevious`, `Survey.customCss` | Server migration (nullable, no backfill) | On deploy |
| CSS processor, API v3 and MCP endpoints, delivery in the environment state | Server | On deploy |
| Survey renderer (`/js/surveys.umd.cjs`): cascade layers, `customCss` prop, `setAppearance`, `data-fb-part` hooks | Server, cached by the CDN | On deploy **and** CDN purge |
| js-core (`/js/formbricks.umd.cjs`): appearance state, passes both CSS scopes | Server, cached by the CDN | On deploy **and** CDN purge |
| `@formbricks/js` wrapper: `appearance` option, `setAppearance` | npm release | When customers upgrade |
| iOS, Android, React Native, Flutter SDKs: appearance, both CSS scopes | Package releases | When apps ship an update |
| Editor cards (workspace Appearance, survey Styling) | Server | On deploy; on Cloud, saving needs the Scale plan |

## Deployment Order

Data and renderer first, so nothing a creator saves reaches a renderer that does not understand it yet.

1. **Server and renderer.** Deploy the migration, processor, API, editor cards and renderer together. Existing surveys must look exactly the same: nothing is saved yet.
2. **Purge the CDN** for `/js/surveys.umd.cjs` and `/js/formbricks.umd.cjs`. Both URLs are unversioned and cached for up to 30 days at the edge, so without a purge some apps and websites keep the old renderer for weeks. Repeat the purge after any renderer rollback.
3. **Stripe entitlement on Cloud**, right after the purge (see below). Until it is attached, every Cloud organization sees the upgrade prompt in the editor cards, Scale included.
4. **SDK packages:** `@formbricks/js`, then the iOS, Android, React Native and Flutter SDKs, each from its own repository. Publication does not have to be atomic. Record every released version in [SDK Compatibility](/docs/surveys/website-app-surveys/sdk-compatibility), replacing "Pending release". Customers whose apps use the mobile SDKs need to ship an app update before their Custom CSS shows in the app; tell them early.
5. **Self-hosted** gets everything with the release that contains steps 1 and 2. There is no plan check, flag or environment variable. Add one release-note line: Custom CSS styles the card; Custom Head Scripts keep scripts, tags and page-level styles.

## Before Enabling On Cloud

* **Stripe entitlement.** Attach the entitlement feature with lookup key `custom-css` to the Scale and Custom products. This is the only gate on Cloud: without it, saving Custom CSS fails with "plan required" for every organization, Scale included. Self-hosted instances skip the plan check.

## Turning It Off

There is no feature flag, so stopping Custom CSS everywhere takes a deploy.

| Problem | Action | Saved CSS |
| - | - | - |
| Custom CSS misbehaves in one survey | Turn off the survey's **Add custom styles**: its CSS stops applying, the workspace CSS stays | Kept |
| Custom CSS misbehaves for one organization | With the customer, restore the previous workspace CSS or clear it in **Appearance** or through the API | Previous revision kept |
| Renderer bug | Roll back the server deploy, then purge the CDN again | Kept |
| Processor policy must change | Change the processor and bump `CUSTOM_CSS_PROCESSOR_VERSION` | Kept as source |
| Delivery must stop everywhere | Deploy a change that withholds it in `resolveRespondentCustomCss` | Kept |

Switching off is not instant: each SDK keeps its configuration until it refreshes (up to 30 minutes on the web, up to an hour in the mobile SDKs).

A processor version bump marks every stored compiled output as stale. Delivery reprocesses stale CSS from its saved source; if the current processor rejects it, the CSS is withheld (never the old output) and creators see a warning in the editor. `BLOCK_EXTERNAL_CUSTOM_CSS_RESOURCES` in `apps/web/modules/custom-css/processor` is a code constant, deliberately without an environment variable or setting. It is part of the effective processor version, so changing it also requires the bump.

## Smoke Checks

Run before each step above:

1. Existing surveys look exactly the same, in light mode, with no Custom CSS saved.
2. Old SDK with the new renderer: a survey opens and answers save, on web, iOS and Android. Mobile shows no Custom CSS.
3. New SDK with an old self-hosted server: surveys stay light, with no errors.
4. A survey with saved CSS and **Add custom styles** off: respondents get the workspace CSS only; turning it back on brings the survey's CSS back.
5. Self-hosted: existing Custom Head Scripts still run.
6. A CMS-style stylesheet (synthetic, never a customer's file) shows one removed-import warning, and its ported hooks style the preview and the live survey in light and dark.

## SDK Handoff

| SDK | Repository | Package | Version | Status |
| - | - | - | - | - |
| JavaScript | `formbricks/js` | `@formbricks/js` | Pending release | Not published |
| iOS | `formbricks/ios` | `FormbricksSDK` | Pending release | Not published |
| Android | `formbricks/android` | `com.formbricks:android` | Pending release | Not published |
| React Native | `formbricks/react-native` | `@formbricks/react-native` | Pending release | Not published |
| Flutter | `formbricks/flutter` | `formbricks` | Pending release | Not published |

Every mobile SDK must pass `customCss: { workspace, survey }` explicitly, from `workspaceSettings.customCss` and each `survey.customCss`, and feature-detect `formbricksSurveys.setAppearance`. The renderer applies Custom CSS only from that prop, so an SDK that does not pass it renders no Custom CSS at all rather than half of it.

## Styling Contract

The `data-fb-part` hook names are declared once, in `FB_PART` (`packages/survey-ui/src/lib/parts.ts`), exported from `@formbricks/survey-ui` and, without any components, from `@formbricks/survey-ui/parts`. Customers' stylesheets depend on these names: renaming or removing one is a breaking change. `parts.test.ts` fails when the customer docs and the constant disagree.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.