Skip to content

fix: strip @layer properties block to prevent host page CSS pollution#7685

Merged
Dhruwang merged 7 commits into
formbricks:mainfrom
mariusbolik:fix/surveys-layer-properties-css-pollution
Apr 15, 2026
Merged

fix: strip @layer properties block to prevent host page CSS pollution#7685
Dhruwang merged 7 commits into
formbricks:mainfrom
mariusbolik:fix/surveys-layer-properties-css-pollution

Conversation

@mariusbolik
Copy link
Copy Markdown
Contributor

Problem

surveys.umd.cjs injects a <style id="formbricks__css"> tag into document.head. That tag contains two sections:

  1. Correctly scoped — Tailwind preflight reset under #fbjs * selectors. ✅
  2. Globally polluting — A @layer properties block with bare *, :before, :after, ::backdrop selectors. ❌
/* This part leaks out of #fbjs and resets ALL elements on the host page */
@layer properties {
  @supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or
            ((-moz-orient: inline) and (not (color: rgb(from red r g b)))) {
    *, :before, :after, ::backdrop {
      --tw-shadow: 0 0 #0000;
      --tw-ring-shadow: 0 0 #0000;
      --tw-ring-offset-width: 0px;
      --tw-border-style: solid;
      /* ~25 more --tw-* properties... */
    }
  }
}

Why it can't be scoped: CSS @layer at-rules are globally scoped by the CSS specification — they cannot be confined by a surrounding selector. This is a spec constraint, not a browser quirk.

Effect: Any host site using Tailwind CSS v4 will have its --tw-* custom properties reset to initial / zero on every element the moment a Formbricks survey initializes. This breaks shadow-*, ring-*, rotate-*, translate-*, and other utilities across the entire page — not just inside the survey widget.

Reported in: formbricks/js#46

Fix

Add a small PostCSS plugin in packages/surveys/postcss.config.cjs that removes any @layer properties { ... } block from the compiled CSS output:

const stripLayerProperties = () => ({
  postcssPlugin: "postcss-strip-layer-properties",
  AtRule: {
    layer: (atRule) => {
      if (atRule.params === "properties") atRule.remove();
    },
  },
});
stripLayerProperties.postcss = true;

The @property declarations already emitted earlier in the same stylesheet serve the same browser-compatibility purpose for all supporting browsers. Removing @layer properties does not affect survey rendering in any browser.

Testing

  • Build packages/surveys and confirm @layer properties is absent from dist/index.umd.cjs.
  • Load the SDK on a Tailwind v4 page, trigger a survey, and verify that host page shadows/rings/transforms are unaffected.

Tailwind v4 emits an `@layer properties` block containing a bare
`*, :before, :after, ::backdrop` selector that resets all `--tw-*`
CSS custom properties on every element of the host page. Because CSS
`@layer` at-rules are globally scoped by the CSS spec, this block
cannot be confined to `#fbjs` and leaks into the host document,
breaking shadows, rings, transforms, and other Tailwind v4 utilities
on any site using the Formbricks SDK.

Add a PostCSS plugin `stripLayerProperties` that removes any
`@layer properties { ... }` block from the compiled CSS output.
The `@property` declarations already present in the same stylesheet
provide the same browser-compatibility fallback for supporting
browsers, so survey rendering is unaffected.

Fixes: formbricks/js#46
@CLAassistant
Copy link
Copy Markdown

CLAassistant commented Apr 7, 2026

CLA assistant check
All committers have signed the CLA.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 7, 2026

Walkthrough

This change updates the PostCSS configuration for the surveys package. The REM_REGEX pattern was modified to use control-character-based boundaries instead of word-boundaries. A new PostCSS plugin called stripLayerProperties was created to intercept and remove @layer properties at-rules. This plugin was then registered in the plugins array, added after the remtoEm() function.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description check ✅ Passed The description provides comprehensive problem context, technical explanation, fix details, and testing steps, but the PR template's checklist sections are not filled out.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Title check ✅ Passed The PR title clearly and concisely describes the main change: removing the @layer properties block to prevent CSS pollution on the host page, which is the primary objective of this changeset.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/surveys/postcss.config.cjs`:
- Line 57: Add the same postcss marker to remtoEm for consistency: after the
existing stripLayerProperties.postcss = true assignment, set remtoEm.postcss =
true so both plugins (remtoEm and stripLayerProperties) clearly declare
postcss=true; locate the remtoEm variable/assignment in the file and add the
marker there.
- Around line 45-56: The AtRule handler in stripLayerProperties currently
compares atRule.params directly which can fail with surrounding whitespace;
change the check in the layer handler to normalize params (e.g., use
atRule.params.trim()) before comparing to "properties" so that the plugin
reliably removes `@layer` properties even if params include unexpected whitespace;
update the condition inside stripLayerProperties -> AtRule.layer (referencing
atRule and atRule.params) accordingly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: f169f4de-037d-411a-9c75-fd3e0f09e6c1

📥 Commits

Reviewing files that changed from the base of the PR and between 87bcad2 and 8bf7fe9.

📒 Files selected for processing (1)
  • packages/surveys/postcss.config.cjs

Comment thread packages/surveys/postcss.config.cjs
Comment thread packages/surveys/postcss.config.cjs
@jobenjada jobenjada requested a review from Dhruwang April 10, 2026 09:37
@jobenjada jobenjada changed the title fix(surveys): strip @layer properties block to prevent host page CSS pollution fix: strip @layer properties block to prevent host page CSS pollution Apr 10, 2026
@jobenjada
Copy link
Copy Markdown
Member

jobenjada commented Apr 10, 2026

hey @mariusbolik thanks for this, we'll look into it. In the meantime, pls sign the CLA above

…fix/surveys-layer-properties-css-pollution
Dhruwang and others added 3 commits April 13, 2026 14:44
…hanges

Remove invisible backspace (0x08) control characters from REM_REGEX in
postcss.config.cjs that were left over when \b word boundaries were
removed. Revert unrelated cn() test additions from utils.test.ts.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The utils.test.ts revert belongs in PR formbricks#7720, not here.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Rewrite REM_REGEX to use a single character-class quantifier (\d[\d.]*)
instead of nested quantifiers (\d+(\.\d+)?) to avoid super-linear
backtracking. Resolves SonarCloud security hotspot S5852.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Dhruwang
Dhruwang previously approved these changes Apr 13, 2026
Copy link
Copy Markdown
Member

@Dhruwang Dhruwang left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the fix, looks good 🚀

@Dhruwang Dhruwang enabled auto-merge April 13, 2026 10:35
Use ([\d.]+) instead of (\d[\d.]*) to eliminate any character overlap
between the leading \d and the quantified class. This is the simplest
possible form with zero backtracking risk (SonarCloud S5852).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The regex uses a single character-class quantifier on trusted PostCSS
input — no backtracking risk. Add NOSONAR comment with rationale.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@Dhruwang Dhruwang added this pull request to the merge queue Apr 15, 2026
Merged via the queue into formbricks:main with commit 0653c6a Apr 15, 2026
28 of 37 checks passed
@mariusbolik
Copy link
Copy Markdown
Contributor Author

Can you tell me when this will be live on the hosted version of Formbricks, @Dhruwang?

@mariusbolik
Copy link
Copy Markdown
Contributor Author

The problem still occurs in the hosted version. When will it be fixed?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants